home *** CD-ROM | disk | FTP | other *** search
/ No Fragments Archive 12: Textmags & Docs / nf_archive_12.iso / MAGS / SOURCES / ATARI_SRC.ZIP / atari source / FALCON / ACC / DRIVERS.ACC / POPMENU.C2 < prev    next >
Encoding:
Text File  |  2001-02-10  |  79.4 KB  |  2,802 lines

  1. /* POPMENU.C
  2.  *==========================================================================
  3.  * DESCRIPTION: Generic Popup Routines ( New and Improved )
  4.  * VERSION 0.00  Started:    February 7, 1991
  5.  * VERSION 0.10            March    4, 1991
  6.  * version 1.00            April    24,1991
  7.  *                Recreated damaged files
  8.  *                Removed open workstation calls.
  9.  *                vhandle = graf_handle()
  10.  *
  11.  *
  12.  * INCLUDE: POPMENU.H
  13.  * Requires: POP_HEAD.H
  14.  *
  15.  */
  16.  
  17.  
  18.  
  19. /* INCLUDE FILES
  20.  *==========================================================================
  21.  */
  22. #include <sys\gemskel.h>
  23. #include <string.h >
  24. #include <tos.h>
  25. #include <stdlib.h>
  26. #include <stdio.h>
  27.  
  28. #include "pop_head.h"
  29. #include "device.h"
  30.  
  31.  
  32. extern int xres, yres;
  33.  
  34.  
  35. /* PROTOTYPES
  36.  *==========================================================================
  37.  */
  38. void    InitPopUpMenus( void );
  39. int    GetNewPopMenu( void );
  40. POP_PTR    GetMenuPtr( int MenuID );
  41.  
  42. BOOLEAN    InitCmdChar( POP_PTR PopPtr );
  43. void    BuildCmdChar( POP_PTR PopPtr );
  44. void    BuildKeyCodes( POP_PTR PopPtr, char *text, int num_entry );
  45. void    DeletePopUpMenu( int MenuID );
  46.  
  47. int     InsertPopUpMenu( char *text, int num_entries, int height );
  48. long    PopUpMenuSelect( int MenuID, int xpos, int ypos, int Item );
  49. BOOLEAN Pop_Blit( long *PopPtr, GRECT *xclip, int flag );
  50.  
  51.  
  52. BOOLEAN Build_Objects( POP_PTR curptr );
  53. void    SetRootNode( OBJECT *tree, int xpos, int ypos, int Width, int Num );
  54. void    SetTextNode( OBJECT *tree, int parent, POP_PTR PopPtr, int obj, int Ypos, int textnum, int Width );
  55. long    CalcTextBufferSize( POP_PTR PopPtr );
  56. void    BuildText( POP_PTR PopPtr );
  57. int    CountBlanksNeeded( char *dindex, char *dtemp, int width, int num );
  58.  
  59.  
  60. BOOLEAN    Pop_Arrow( POP_PTR PopPtr, int obj );
  61. void    UpArrowStatus( POP_PTR PopPtr );
  62. void    DownArrowStatus( POP_PTR PopPtr );
  63. void    SetArrowClickDelay( long delay );
  64.  
  65. /* KEYCODE CHECKING */
  66. long    CheckForKeyCodes( POP_PTR PopPtr, char *text, int num_entry );
  67. char    *CheckForCtrl( char *text );
  68. char    *CheckForAlt( char *text );
  69. char    *CheckForCapKey( char *text );
  70. char    *CheckForCmdKey( char *text );
  71. BOOLEAN CheckForDisable( char *text );
  72. BOOLEAN    CheckForMenuCheck( char *text );
  73. char     *CheckForSubMenu( char *text );
  74. char    *CheckForFuncKey( char *text );
  75. BOOLEAN    CheckForDuplicate( char *text );
  76.    
  77. /* APPEARANCE OF ITEMS */
  78. void    SetHeight( int MenuID, int Height );
  79. void    SetNumItems( int MenuID, int NumItems );
  80.  
  81. void    CheckItem( int MenuID, int item, BOOLEAN check );
  82. void    DisableItem( int MenuID, int item );
  83. void    EnableItem( int MenuID, int item );
  84.    
  85. void    SetItemCmd( int MenuID, int item, char cmd );
  86. char    GetItemCmd( int MenuID, int item );
  87.  
  88. void    SetSubMenuID( int MenuID, int item, int ID );
  89. int    GetSubMenuID( int MenuID, int item );
  90.  
  91. void    SetItemMark( int MenuID, int item, char key );
  92. char    GetItemMark( int MenuID, int item );
  93.  
  94. void    SetFuncMark( int MenuID, int item, int num );
  95. int     GetFuncMark( int MenuID, int item );
  96.  
  97. void    SetItem( int MenuID, int item, char *text );
  98. char    *GetItem( int MenuID, int item );
  99.  
  100. int    GetStartItem( int MenuID );
  101. void    SetStartItem( int MenuID, int item );
  102.  
  103.  
  104. /* SUBMENU HANDLING */
  105. POP_PTR    DoSubMenu( POP_PTR PopPtr, int obj );
  106. BOOLEAN    ShowSubMenu( int MenuID, int xpos, int ypos, GRECT *rect );
  107. void    HideSubMenu( POP_PTR PopPtr );
  108. long    EvntSubMenu( POP_PTR PopPtr );
  109. int    FindNum( POP_PTR PopPtr, int obj );
  110. long    MenuChoice( void );
  111.  
  112.  
  113. /* SUBMENU DELAY */
  114. long    GetTimeHz( void );
  115. void    SetSubMenuDelay( long ms );
  116. void    SetSubDragDelay( long ms );
  117.  
  118.  
  119. void    AdjustToScreen( POP_PTR PopPtr, int xpos, int ypos, GRECT *rect, BOOLEAN byte_flag, BOOLEAN Display_Flag );
  120. void    WaitForUpButton( void );
  121.  
  122. /* EXTERNALS
  123.  *==========================================================================
  124.  * These functions and variables are required and must be
  125.  * supplied externally.
  126.  */
  127.  
  128. int    BUT_OR( void );        /* Used to ensure left mouse button */
  129. extern  int   (*BADDR)( void );
  130. extern  int    BSTATE;
  131. extern  int    BREAL;
  132.  
  133.  
  134. /* DEFINES
  135.  *==========================================================================
  136.  */
  137. #define INIT_DISPLAY_DELAY  300L    /* Initial Submenu Display Delay   */ 
  138. #define INIT_DRAG_DELAY     3000L   /* Initial Submenu Drag Delay      */
  139.  
  140. #define INIT_CLICK_DELAY    150L   /* Delay for STARTING arrow scroll */
  141.  
  142. #define STLOW    2
  143. #define STMED    3
  144.  
  145. #define MAX_MENUS    50       /* Maximum # of Menus allowed...*/
  146. #define UP_ARROW    0x01
  147. #define DOWN_ARROW    0x02
  148. #define RIGHT_ARROW    0x03
  149.  
  150.  
  151.  
  152. /* GLOBAL
  153.  *==========================================================================
  154.  */
  155. POP_NODE MenuList[ MAX_MENUS ];        /* Menu structure Storage */
  156. BOOLEAN  free_flag[ MAX_MENUS ];
  157.  
  158. char *KeyText[] = { "HOME",        /* Text for menu items using these*/
  159.             "HELP",        /* as keyboard shortcuts...       */
  160.             "UNDO",
  161.             "ESC",
  162.             "INSERT",
  163.             "CLR",
  164.             "DEL",
  165.             "TAB",
  166.             "ENTER",
  167.             "RETURN"
  168.           };
  169.  
  170.  
  171. char CmdText[ 10 ];        /* Temp string storage...       */
  172. char TempString[ 255 ];
  173. int  xout[57];            /* used for vq_extnd() */
  174.  
  175.  
  176. long SUBMENU_DELAY;        /* Delay time for submenus to appear ( ms )    */
  177. long SUBDRAG_DELAY;        /* Delay time for submenus to go active( ms)   */
  178. long CLICK_DELAY;        /* Delay time for arrows to start scrolling(ms)*/
  179. long TimeInHz;            /* Current Time in 200 Hz */
  180.  
  181.                 /* Used by the MenuChoice() call         */
  182. int  CurObject;            /* Current Object clicked on. ( Global ) */
  183. int  CurMenu;            /* Current Menu clicked on. ( Global )   */
  184.  
  185.  
  186. /* FUNCTIONS
  187.  *==========================================================================
  188.  */
  189.  
  190.  
  191. /* InitPopMenus()
  192.  * ====================================================================
  193.  * Simply clears out the flags in the free_flags structure.
  194.  */
  195. void
  196. InitPopUpMenus( void )
  197. {
  198.    int i;
  199.    
  200.    for( i = 0; i < MAX_MENUS; i++ )
  201.       free_flag[ i ] = FALSE;
  202.    SetSubMenuDelay( INIT_DISPLAY_DELAY );
  203.    SetSubDragDelay( INIT_DRAG_DELAY );        
  204.    SetArrowClickDelay( INIT_CLICK_DELAY );
  205. }
  206.  
  207.  
  208.  
  209. /* InsertPopMenu()
  210.  * ====================================================================
  211.  * Returns: Pointer to the POPUP structure.
  212.  * IN:   
  213.  *       XHEIGHT ( in characters ) - actually, number of entries displayed
  214.  *     NUM_ENTRIES           - Total # of menu entries.
  215.  *     Pointer to text address...
  216.  * OUT: # >= 0    Valid Menu ID
  217.  *      # < 0     Error messages.
  218.  *                -1 = Memory Allocation Error.
  219.  *          -2 = No more Menu Slots available.
  220.  */
  221. int
  222. InsertPopUpMenu( char *text, int num_entries, int height )
  223. {
  224.     POP_PTR PopPtr;
  225.     int     MenuID;
  226.     
  227.     MenuID = GetNewPopMenu();
  228.     
  229.     /* No more menu slots available.*/
  230.     if( MenuID == -1 )
  231.        return( -2 );
  232.  
  233.     PopPtr = GetMenuPtr( MenuID );
  234.     PPREV( PopPtr )   = ( POP_PTR )NULL;
  235.        
  236.     PMENUID( PopPtr ) = MenuID;     
  237.     PXPOS( PopPtr )   = PYPOS( PopPtr ) = 0;
  238.     PNUM( PopPtr )    = num_entries;
  239.     POBJECT( PopPtr ) = ( OBJECT *)NULL;
  240.     
  241.     /* Take care of height of menu */ 
  242.     if( height > PNUM( PopPtr ) )
  243.         height = PNUM( PopPtr );
  244.     PHEIGHT( PopPtr )     = height;
  245.     PIX_HEIGHT( PopPtr )  = ( PHEIGHT( PopPtr ) * gl_hchar );
  246.     PTEXT( PopPtr )       = text;
  247.     
  248.     if( !InitCmdChar( PopPtr ))
  249.     {
  250.        /* If memory error, clear out the Menu Slot
  251.         * and return a -1.
  252.         */
  253.        free_flag[ MenuID ] = FALSE;       
  254.        MenuID = -1;
  255.     }
  256.     return( MenuID );
  257. }
  258.  
  259.  
  260.  
  261. /* GetNewPopMenu()
  262.  * ====================================================================
  263.  * Find an unused slot for a menu.
  264.  * RETURN( -1 ) if there are no more menu slots available.
  265.  */
  266. int
  267. GetNewPopMenu( void )
  268.     int i;
  269.     
  270.     for( i = 0; i < MAX_MENUS; i++ )
  271.     {
  272.     if( !free_flag[ i ] )
  273.     {
  274.        free_flag[i] = TRUE;
  275.        return( i );
  276.     }   
  277.     }
  278.     return( -1 );
  279. }
  280.  
  281.  
  282.  
  283.  
  284. /* GetMenuPtr()
  285.  * ====================================================================
  286.  * Returns a pointer to the Menu Structure if given a valid menuID
  287.  */
  288. POP_PTR
  289. GetMenuPtr( int MenuID )
  290. {
  291.     return( ( POP_PTR )&MenuList[ MenuID ] );
  292. }
  293.  
  294.  
  295.  
  296. /* InitCmdChar()
  297.  * ====================================================================
  298.  * Initializes the CmdChar memory for a menu structure.
  299.  * The memory is MALLOCed! So the application MUST call
  300.  * DeletePopMenu() for each menu created. OR ELSE...!
  301.  * Need to check for malloc failures.
  302.  * RETURN: FALSE = ERROR!
  303.  *       TRUE  = AOK!
  304.  * It will be up to the program to inform the user that
  305.  * no menu was created and initialized.
  306.  */
  307. BOOLEAN
  308. InitCmdChar( POP_PTR PopPtr )
  309. {
  310.     int i;
  311.     
  312.     PCmdChar( PopPtr ) = ( CmdChar *)malloc( (long)( (long)PNUM( PopPtr ) + 10L ) * (long)sizeof( CmdChar ));
  313.     /* We go up to + 2 because we have ...
  314.      * PNUM() = UP ARROW TEXT
  315.      * PNUM+1 = DOWN ARROW TEXT
  316.      * PNUM+2 = BLANK TEXT
  317.      */
  318.     if( PCmdChar( PopPtr ) )
  319.     { 
  320.       for( i = 0; i <= ( PNUM( PopPtr ) + 2 ); i++ )
  321.       {
  322.          CmdState( PopPtr->CharPtr[ i ] ) = NORMAL;
  323.          CmdSubMenu( PopPtr->CharPtr[ i ] ) = -1;
  324.          CmdFlag( PopPtr->CharPtr[i] ) = '\0';
  325.       }
  326.       
  327.       BuildCmdChar( PopPtr );
  328.       return( TRUE );
  329.     }
  330.     else
  331.       return( FALSE );
  332. }
  333.  
  334.  
  335.  
  336.  
  337. /* BuildCmdChar()
  338.  * ====================================================================
  339.  * Calculate the text buffer size that we'll have to malloc, based upon
  340.  * the largest text string and the number of entries.
  341.  */
  342. void
  343. BuildCmdChar( POP_PTR PopPtr )
  344. {
  345.    char *txtptr;
  346.    long length;
  347.    int  count;
  348.    
  349.    txtptr = PTEXT( PopPtr );
  350.    if( txtptr )
  351.    {
  352.        for( count = 0; count < PNUM( PopPtr ); count++ )
  353.        {    
  354.          length = strlen( txtptr );
  355.          BuildKeyCodes( PopPtr, txtptr, count );
  356.          txtptr += ( length + 1 );
  357.        }
  358.    }
  359. }
  360.  
  361.  
  362.  
  363.  
  364.  
  365. /* BuildKeyCodes()
  366.  * =====================================================================
  367.  * Checks the text string for various keycodes and subtracts
  368.  * an appropriate amount from the length if any are found.
  369.  * For many of these, there should only be one of them.
  370.  * We then ADD back in the amount of ACTUAL text usage this item
  371.  * would take. IE: a Function Key can be F10 so 3 characters...
  372.  * or a CmdKey can be the letters: 'DELETE' 6 characters added.
  373.  * This will also fill up the CMDCHAR structures.
  374.  */
  375. void
  376. BuildKeyCodes( POP_PTR PopPtr, char *text, int num_entry )
  377. {
  378.     char *txtptr;
  379.  
  380.     if( ( txtptr = CheckForCtrl( text )) != NULL )
  381.     {
  382.         CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '^';
  383.         CmdHotKey( PopPtr->CharPtr[ num_entry ] ) = *( ++txtptr );
  384.     }
  385.     
  386.     
  387.     if( ( txtptr = CheckForAlt( text ) ) != NULL )
  388.     {
  389.          CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '&';
  390.          CmdHotKey( PopPtr->CharPtr[ num_entry ] ) = *( ++txtptr );
  391.     }
  392.          
  393.     if( ( txtptr = CheckForCapKey( text ) ) != NULL )
  394.     {
  395.          CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '|';
  396.          CmdHotKey( PopPtr->CharPtr[ num_entry ] ) = *( ++txtptr );
  397.     }
  398.     
  399.     if( ( txtptr = CheckForCmdKey( text ) ) != NULL )
  400.     {
  401.         /* We ACKNOWLEDGE -----
  402.          * [H] HELP    [U] Undo    [E] Escape    [I] Insert
  403.          * [D] DEL    [C] Clr        [h] Home    [T] TAB
  404.          * [e] ENTER    [R] RETURN
  405.          */
  406.         CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '$';
  407.         CmdHotKey( PopPtr->CharPtr[ num_entry ] ) = *( ++txtptr ); 
  408.     }
  409.     
  410.     
  411.     /* Check for the '@' character which will tell us to
  412.      * disable the current menu item that we are working on.
  413.      */
  414.     
  415.     if( CheckForDisable( text ) )
  416.       DisableItem( PMENUID( PopPtr ), num_entry );
  417.     else   
  418.       EnableItem( PMENUID( PopPtr ), num_entry );
  419.  
  420.     /* Check for CheckMark  ( ! ) */
  421.     CheckItem( PMENUID( PopPtr ), num_entry, CheckForMenuCheck( text ) );
  422.  
  423.     if( ( txtptr = CheckForSubMenu( text ) ) != NULL )
  424.     {
  425.     CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '~';
  426.     txtptr++;
  427.     CmdSubMenu( PopPtr->CharPtr[ num_entry ] ) = atoi( txtptr );
  428.     } 
  429.     
  430.     if( ( txtptr = CheckForFuncKey( text ) ) != NULL )
  431.     {
  432.         CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '#';
  433.         txtptr++;
  434.         CmdFuncKey( PopPtr->CharPtr[ num_entry ] ) = atoi( txtptr );
  435.     }
  436.     
  437.     if( CheckForDuplicate( text ) )
  438.         CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '*';
  439. }
  440.  
  441.  
  442.  
  443.  
  444.  
  445.  
  446. /* DeletePopUpMenu()
  447.  * ====================================================================
  448.  */
  449. void
  450. DeletePopUpMenu( int MenuID )
  451. {
  452.    POP_PTR PopPtr;
  453.    
  454.    if( free_flag[ MenuID ] )
  455.    {
  456.      free_flag[ MenuID ] = FALSE;
  457.      PopPtr = GetMenuPtr( MenuID );
  458.      
  459.      if( PCmdChar( PopPtr ) )
  460.        free( PCmdChar( PopPtr ) );
  461.        
  462.      PCmdChar( PopPtr ) = ( CmdChar *)NULL;
  463.    }  
  464. }
  465.  
  466.  
  467.  
  468.  
  469. /* PopUpMenuSelect()
  470.  * ====================================================================
  471.  * Displays the actual popup menu.
  472.  * Let's the user select an object.
  473.  * IN:   POP_PTR PopPtr;
  474.  *       int     xpos;
  475.  *       int     ypos;
  476.  *       int     Start_Obj;  Index to show selected else start with 0
  477.  *       BOOLEAN Arrow_Flag; If TRUE, display arrows if height < num_items.
  478.  *                    FALSE, don't display arrows if height < num_items.
  479.  * returns:  long where....
  480.  *        HIGH WORD    LOW WORD
  481.  *         MenuID          Item Selected.
  482.  *           -1L if NONE selected.
  483.  *         If the MenuID is valid, but the item is -1, then
  484.  *         the user clicked on a disabled menu item.
  485.  *         -2L if Memory Allocation Error
  486.  *         -3L if workstation error.
  487.  */
  488. long
  489. PopUpMenuSelect( int MenuID, int xpos, int ypos, int Item )
  490. {
  491.     
  492.     long    obj;
  493.     POP_PTR PopPtr;
  494.     GRECT   rect;
  495.     MRETS   mk;
  496.     int     max_height;
  497.         
  498.     WaitForUpButton();
  499.     wind_update( BEG_MCTRL );
  500.  
  501.     open_vwork();
  502.     vq_extnd( vhandle, 0, xout );
  503.     xres = xout[0];
  504.     yres = xout[1];    
  505.  
  506.     vex_butv( vhandle, BUT_OR, &BADDR );    
  507.     
  508.     PopPtr = GetMenuPtr( MenuID );
  509.     PopUpItem( PopPtr ) = Item;
  510.  
  511.     PXPOS( PopPtr )   = xpos;
  512.     PYPOS( PopPtr )   = ypos;
  513.     PPREV( PopPtr )   = ( POP_PTR )NULL;
  514.  
  515.     /* Take care of height of menu */ 
  516.     if( PHEIGHT( PopPtr ) > PNUM( PopPtr ) )
  517.         PHEIGHT( PopPtr ) = PNUM( PopPtr );
  518.  
  519.     /* Limit Height to Height of screen - ( 2 * gl_hchars ) */
  520.     max_height = yres - ( 2 * gl_hchar );
  521.     while( ( PHEIGHT( PopPtr ) * gl_hchar ) > max_height )
  522.        PHEIGHT( PopPtr ) -= 1;
  523.  
  524.     PIX_HEIGHT( PopPtr )  = ( PHEIGHT( PopPtr ) * gl_hchar );
  525.  
  526.        
  527.     /* Take care of the width of the menu here...*/
  528.     PTEXTBUFFSIZE( PopPtr ) = CalcTextBufferSize( PopPtr );
  529.     PIX_WIDTH( PopPtr )     = ( PWIDTH( PopPtr ) * gl_wchar );
  530.  
  531.     AdjustToScreen( PopPtr, xpos, ypos, &rect, FALSE, TRUE );
  532.     if( Pop_Blit( (long *)&PMEM( PopPtr ), &PObRect( PopPtr ), 0 ) )
  533.     {
  534.        if( Build_Objects( PopPtr ) )
  535.        {
  536.          obj = EvntSubMenu( PopPtr );
  537.          if( obj == -2L ) /* returning from a submenu - error */
  538.              obj = -1L;    /* then set it to click on nothing */
  539.          Pop_Blit( (long *)&PMEM( PopPtr ), &PObRect( PopPtr ), 1 );
  540.        }
  541.        else    /* Memory Allocation Error in Space Capsule */
  542.          obj = -2L;  
  543.  
  544.        if( POBJECT( PopPtr ) )
  545.            free( POBJECT( PopPtr ) );
  546.         
  547.        if( PTEXTBUFF( PopPtr ) )    
  548.            free( PTEXTBUFF( PopPtr ) );
  549.         
  550.     }
  551.     else    /* ERROR: Memory Allocation Error */
  552.       obj = -2L;
  553.  
  554.     POBJECT( PopPtr )   = ( OBJECT *)NULL;
  555.     PTEXTBUFF( PopPtr ) = ( char *)NULL;
  556.     
  557.     Evnt_button( 1, 1, 0, &mk );
  558.  
  559.     vex_butv( vhandle, BADDR, &BADDR );
  560.     close_vwork();
  561.     
  562.     wind_update( END_MCTRL );
  563.  
  564.     return( obj );
  565. }
  566.  
  567.  
  568.  
  569.  
  570. /* Build_Objects()
  571.  * ====================================================================
  572.  * RETURN: TRUE - AOK
  573.  *         FALSE- Memory Allocation Error
  574.  */
  575. BOOLEAN
  576. Build_Objects( POP_PTR PopPtr )
  577. {
  578.    int     num_objects;
  579.    GRECT   rect;
  580.    int     text_index;        /* Index into the text data 0 based */
  581.    int     count;
  582.    int     ypos;        /* Y position of object ( in characters*/
  583.    int     temp;
  584.           
  585.    OBJECT *tree;
  586.    int    obj;
  587.  
  588.    /* Calculate the number of objects needed...
  589.     * ROOT + # of STRINGS + ( safety buffer )
  590.     */
  591.    num_objects = 1 + PHEIGHT( PopPtr ) + 10; /* + 10 for safety buffer */
  592.  
  593.    /* get the memory for this number of objects */   
  594.    POBJECT( PopPtr ) = ( OBJECT * )malloc( (long)(sizeof( OBJECT ) * (long)num_objects )); 
  595.     
  596.    /* Malloc the text memory and then build them. */
  597.    /* Need malloc checking of course...           */
  598.    PTEXTBUFF( PopPtr) = malloc( PTEXTBUFFSIZE( PopPtr ) );
  599.    
  600.    /* Memory Allocation Error */
  601.    if( !POBJECT( PopPtr ) || !PTEXTBUFF( PopPtr ) )
  602.        return( FALSE );
  603.  
  604.    BuildText( PopPtr );
  605.  
  606.        
  607.    /* setup the root */
  608.    tree = POBJECT( PopPtr );
  609.    SetRootNode( tree, PXPOS( PopPtr ), PYPOS( PopPtr ),
  610.                 PWIDTH( PopPtr ), PHEIGHT( PopPtr ) );
  611.    
  612.    ypos = text_index = 0;
  613.    
  614.    obj = 1;            /* Start Creating from object 1 */
  615.    PFIRST( PopPtr ) = 1;    /* first G_STRING starts at 3   */
  616.  
  617.    POFFSET( PopPtr )   = ( PopUpItem( PopPtr ) + ( PNUM( PopPtr ) > PHEIGHT( PopPtr ) )) / PHEIGHT( PopPtr );
  618.  
  619.    if( POFFSET( PopPtr ) )
  620.    {
  621.        POFFSET( PopPtr ) = PopUpItem( PopPtr );
  622.        temp = PNUM( PopPtr ) - PHEIGHT( PopPtr ) + 1;
  623.  
  624.        if( POFFSET( PopPtr ) >= temp )
  625.            POFFSET( PopPtr ) = temp;
  626.    }      
  627.    text_index = POFFSET( PopPtr );
  628.    
  629.    for( count = 0; count < PHEIGHT( PopPtr ); count++ )
  630.    {
  631.       if( count == 0 )
  632.       {
  633.        if( POFFSET( PopPtr ) )
  634.              SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, PNUM( PopPtr ), PWIDTH( PopPtr ) );
  635.            else  
  636.            {
  637.              if( CmdState( PopPtr->CharPtr[ text_index ] ) & CHECKED )
  638.                  CheckObj( obj );
  639.              SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, text_index++, PWIDTH( PopPtr ) );
  640.            }  
  641.       }  
  642.       else
  643.       {
  644.         if( count >=  ( PHEIGHT( PopPtr ) - 1 ) )
  645.         {
  646.            if( ( POFFSET( PopPtr ) + PHEIGHT( PopPtr ) ) < PNUM( PopPtr ) )
  647.               SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, PNUM( PopPtr ) + 1, PWIDTH( PopPtr ) );
  648.            else
  649.            {
  650.               if( CmdState( PopPtr->CharPtr[ text_index ] ) & CHECKED )
  651.                  CheckObj( obj );
  652.  
  653.               SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, text_index++, PWIDTH( PopPtr ) );
  654.            }   
  655.         }
  656.         else      
  657.         {
  658.            if( CmdState( PopPtr->CharPtr[ text_index ] ) & CHECKED )
  659.                CheckObj( obj );
  660.  
  661.            SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, text_index++, PWIDTH( PopPtr ) );
  662.         } 
  663.       }
  664.    }   
  665.    PLAST( PopPtr ) = obj - 1;
  666.    ObFlags( obj - 1 ) = LASTOB;
  667.  
  668.    rect = ObRect( ROOT );
  669.    rect.g_x -= 2;
  670.    rect.g_y -= 2;
  671.    rect.g_w += 5;
  672.    rect.g_h += 5;
  673.    rc_intersect( &desk, &rect );        /* clip to desktop */
  674.    Objc_draw( tree, ROOT, MAX_DEPTH, &rect );
  675.    return( TRUE );
  676. }
  677.  
  678.  
  679.  
  680. /* SetRootNode()
  681.  * ====================================================================
  682.  */
  683. void
  684. SetRootNode( OBJECT *tree, int xpos, int ypos, int Width, int Num )
  685. {
  686.     ObNext( ROOT )  = -1;
  687.     ObHead( ROOT )  = -1;
  688.     ObTail( ROOT )  = -1;
  689.     ObType( ROOT )  = G_BOX;
  690.     ObFlags( ROOT ) = NONE;
  691.     ObState( ROOT ) = SHADOWED;
  692.     ObIndex( ROOT ) = 0xFF1100L;
  693.  
  694.     /* fix up for screen...*/
  695.     ObX( ROOT ) = xpos;
  696.     ObY( ROOT ) = ypos;
  697.     ObW( ROOT ) = Width * gl_wchar;
  698.     ObH( ROOT ) = Num * gl_hchar;
  699. }
  700.  
  701.  
  702.  
  703. /* SetTextNode()
  704.  * ====================================================================
  705.  */
  706. void
  707. SetTextNode( OBJECT *tree, int parent, POP_PTR PopPtr, int obj, int Ypos, int textnum, int Width )
  708. {
  709.        ObNext( obj ) = -1;
  710.        ObHead( obj ) = -1;
  711.        ObTail( obj ) = -1;
  712.  
  713.        ObType( obj )   = G_STRING;
  714.        ObFlags( obj )  = NONE;
  715.        
  716.        ObString( obj ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * textnum ];
  717.  
  718.        ObState( obj )  = CmdState( PopPtr->CharPtr[ textnum ] );
  719.  
  720.        ObX( obj )      = 0;
  721.        ObY( obj )      = Ypos * gl_hchar;
  722.        ObW( obj )      = Width * gl_wchar;
  723.        ObH( obj )      = gl_hchar;
  724.        objc_add( tree, parent, obj );
  725. }
  726.  
  727.  
  728.  
  729.  
  730. /* CalcTextBufferSize()
  731.  * ====================================================================
  732.  * Calculate the text buffer size that we'll have to malloc, based upon
  733.  * the largest text string and the number of entries.
  734.  */
  735. long
  736. CalcTextBufferSize( POP_PTR PopPtr )
  737. {
  738.    char *xptr;
  739.    long size;
  740.    long xsize;
  741.    long actual;
  742.    int  i;
  743.    
  744.    size = 0L;
  745.    PWIDTH( PopPtr ) = 0;
  746.    xptr = ( char *)NULL;
  747.    xptr = PTEXT( PopPtr );
  748.    if( xptr )
  749.    {
  750.        for( i = 0; i < PNUM( PopPtr ); i++ )
  751.        {    
  752.          xsize = strlen( xptr );
  753.          actual = CheckForKeyCodes( PopPtr, xptr, i );
  754.          if( actual > size )
  755.             size = actual;
  756.          xptr += ( xsize + 1 );
  757.  
  758.          /* Get's us past the nulls to the head of the next string */
  759.          while( !( *xptr ) )
  760.            xptr++;         
  761.        }
  762.        /* size-> now contains the length of the longest string. */
  763.        size += 1;              /* for a blank after...*/
  764.        size += 2;           /* for 2 blanks before the string*/
  765.        /* Number of characters in our longest string...*/
  766.        PWIDTH( PopPtr ) = (int)size;
  767.  
  768.        size += 1;            /* for the null.       */
  769.        size += 10;            /* for the Control Keys or RT Arrow */
  770.        size *= PNUM( PopPtr );  /* For the number of entries. */
  771.        size += ( size / 2 );    /* Buffer area, just in case  */  
  772.    }
  773.    return( size );
  774. }
  775.  
  776.  
  777. /* BuildText()
  778.  * ====================================================================
  779.  * Takes the pointer to the menu_text given by the program and
  780.  * builds the text in the memory buffer where...
  781.  *  1) 2 spaces before each text string.
  782.  *  2) 1 space after the longest string.
  783.  *  3) pads spaces after shorter strings to match the longest string.
  784.  *  4) Null terminates each string.
  785.  *  5) *# - The char following is duplicated the width of the menu.
  786.  *        Characters following it are ignored to the end of the string.
  787.  *        example: *-    The line is filled with "---------"
  788.  *  6) @  - means to disable this menu item
  789.  *  7) /# - means that there is a keycode: ie: /T ==   [T]
  790.  *  8) ## - means that there is a function key.  #10 is F10
  791.  *  9) ^# - means that there is a ctrl key:  ^H
  792.  * 10) $# - means that there is a CmdKey: ie: $H = HELP ( 4 chars )
  793.  *     We ACKNOWLEDGE -----
  794.  *     [H] HELP    [U] Undo    [E] Escape    [I] Insert
  795.  *     [D] DEL        [C] Clr        [h] Home    [T] TAB
  796.  *     [e] ENTER    [R] RETURN
  797.  *
  798.  * 11) &# - means that there is an ALT Key:  &A - Alt A
  799.  * 12) ~# - means that there is a submenu: ~1 - attach submenu 1
  800.  * 13) !  - means that the menu item is CHECKED.
  801.  */
  802. void
  803. BuildText( POP_PTR PopPtr )
  804. {
  805.    char *sindex;        /* Pointer to source text...    */
  806.    char *stemp;            /* Pointer INTO source text     */
  807.    char *dindex;        /* Pointer to destination text  */
  808.    char *dtemp;            /* Pointer INTO destination text*/
  809.    int  num_entries;        /* Number of entries counter... */
  810.    int  num;
  811.    int  num2;
  812.    long length;            /* length of a string...    */
  813.    long i;            /* temp variable...        */
  814.    
  815.    if( !PTEXTBUFF( PopPtr ) )    /* if there is no destination buffer...*/
  816.        return;            /* skip doing all of this...           */
  817.           
  818.    sindex = PTEXT( PopPtr );
  819.    dindex = PTEXTBUFF( PopPtr );
  820.  
  821.    for( num_entries = 0; num_entries <= PNUM( PopPtr ); num_entries++ )
  822.    {
  823.      if( num_entries < PNUM( PopPtr ) )
  824.      {
  825.      
  826.        if( ( stemp = strchr( sindex, '*' )) != NULL )
  827.        {
  828.          /* Check for a '*' character. The character following
  829.           * will be extened the length of the string.
  830.           * All characters following after that will be ignored.
  831.           */
  832.          stemp++;     /* gets us to the item we want to duplicate */ 
  833.          dtemp = dindex; /* get a ptr to the destination             */
  834.  
  835.      /* Duplicate the character to the proper width wanted...*/         
  836.          for( i = 0; i < PWIDTH( PopPtr ); i++ )
  837.             *dtemp++ = *stemp;
  838.          *dtemp = '\0';       /* NULL terminate the string */
  839.        }  
  840.        else
  841.        {
  842.          strcpy( dindex, "  " );    /* Pad the entry with 2 blanks */
  843.          stemp = sindex;        /* Get a pointer to the source */
  844.          dtemp = dindex+2;        /* Get a pointer to the dest...*/
  845.  
  846.      /* Copy the source string to the destination buffer
  847.       * skipping the HOT chars that will allow submenus,
  848.       * disabling menus, keycodes etc.
  849.       */
  850.      while( *stemp )
  851.      {
  852.         switch( *stemp )
  853.         {
  854.            case '@': /* Disable menu item */
  855.            case '!': /* CheckMark menu item */
  856.                     stemp++;
  857.                     break;
  858.     
  859.            case '^': /* Ctrl Key */
  860.            case '&': /* Alt Key  */
  861.            case '|': /* Cap Key  */
  862.            case '$': /* CmdKey   */
  863.            case '~': /* Submenu  */
  864.                     stemp += 2;
  865.                     break;
  866.                     
  867.            case '#': /* FuncKey  */
  868.                     stemp += 2;
  869.                     num = CmdFuncKey( PopPtr->CharPtr[ num_entries ] );
  870.                     if( num > 9 )
  871.                        stemp++;
  872.                     break;
  873.                             
  874.            default: *dtemp++ = *stemp++;
  875.                    break;
  876.         }
  877.      }
  878.  
  879.      /* Now add in the necessary text for the Keycodes..*/
  880.          switch( CmdFlag( PopPtr->CharPtr[ num_entries ] ) )
  881.          {
  882.            case '^': /* Ctrl Key */
  883.            case '&': /* Alt Key  */
  884.              num = CountBlanksNeeded( dindex, dtemp, PWIDTH( PopPtr ), 2 );
  885.              for( i = 0; i < num; i++ )                    
  886.                 *dtemp++ = ' ';     /* add a space */
  887.                      *dtemp++ = (( CmdFlag( PopPtr->CharPtr[ num_entries ] ) == '^' ) ? ( '^' ) : ( 0x07 ) );   
  888.                      *dtemp++ = CmdHotKey( PopPtr->CharPtr[ num_entries ] );
  889.                     break;
  890.                     
  891.            case '|': /* Cap Key  */
  892.              num = CountBlanksNeeded( dindex, dtemp, PWIDTH( PopPtr ), 3 );
  893.              for( i = 0; i < num; i++ )                    
  894.                 *dtemp++ = ' ';     /* add a space */
  895.                     *dtemp++ = '[';
  896.                     *dtemp++ = CmdHotKey( PopPtr->CharPtr[ num_entries ] );
  897.                     *dtemp++ = ']';
  898.                     break;
  899.                     
  900.            case '$': /* CmdKey   */
  901.                     switch( CmdHotKey( PopPtr->CharPtr[ num_entries ] ) )
  902.                     {
  903.                        case 'h': /* Home */
  904.                                  strcpy( &CmdText[0], KeyText[0] ); 
  905.                                  break;
  906.                                  
  907.                        case 'H': /* HELP */
  908.                                  strcpy( &CmdText[0], KeyText[1] );
  909.                                  break;
  910.                                  
  911.                        case 'U': /* Undo */
  912.                                  strcpy( &CmdText[0], KeyText[2] );
  913.                                  break;
  914.                                  
  915.                        case 'E': /* Escape*/
  916.                                  strcpy( &CmdText[0], KeyText[3] );
  917.                                  break;
  918.                                  
  919.                        case 'I': /* Insert */
  920.                                  strcpy( &CmdText[0], KeyText[4] );
  921.                                  break;
  922.                                  
  923.                        case 'C': /* Clr */
  924.                                  strcpy( &CmdText[0], KeyText[5] );
  925.                                  break;
  926.                                  
  927.                        case 'D': /* Delete */
  928.                                  strcpy( &CmdText[0], KeyText[6] );
  929.                                  break;
  930.                                  
  931.                 case 'T': /* TAB    */
  932.                           strcpy( &CmdText[0], KeyText[7] );
  933.                            break;
  934.  
  935.                 case 'e': /* ENTER  */
  936.                           strcpy( &CmdText[0], KeyText[8] );
  937.                            break;
  938.  
  939.                 case 'R': /* RETURN */        
  940.                           strcpy( &CmdText[0], KeyText[9] );
  941.                            break;
  942.  
  943.                        default:  CmdText[0] = '\0';
  944.                                  break;
  945.                     }
  946.                     num = (int)strlen( &CmdText[0] );
  947.              num = CountBlanksNeeded( dindex, dtemp, PWIDTH( PopPtr ), num );
  948.              for( i = 0; i < num; i++ )                    
  949.                 *dtemp++ = ' ';     /* add a space */
  950.              *dtemp = '\0';   
  951.              strcat( dindex, &CmdText[0] );
  952.              length = strlen( dindex );
  953.              dtemp = dindex + length;             
  954.                     break;
  955.                     
  956.            case '#': /* FuncKey  */
  957.              num = CmdFuncKey( PopPtr->CharPtr[ num_entries ] );
  958.              num = (( num < 10 ) ? ( 2 ) : ( 3 ) );
  959.              num2 = CountBlanksNeeded( dindex, dtemp, PWIDTH( PopPtr ), num );
  960.              for( i = 0; i < num2; i++ )                    
  961.                 *dtemp++ = ' ';     /* add a space */
  962.                     *dtemp++ = 'F';
  963.                     num = CmdFuncKey( PopPtr->CharPtr[ num_entries ] );
  964.                     if( num < 10 )
  965.                        *dtemp++ = num + '0';
  966.                     else    /* handle special case for F10 */
  967.                     {
  968.                        *dtemp++ = '1';
  969.                        *dtemp++ = '0';
  970.                     }   
  971.                     break;
  972.  
  973.           case '~': /* Submenu */
  974.             num = CountBlanksNeeded( dindex, dtemp, PWIDTH( PopPtr ), 2 );
  975.             for( i = 0; i < num; i++ )                    
  976.                 *dtemp++ = ' ';     /* add a space */
  977.                     *dtemp++ = RIGHT_ARROW;    /* Right Arrow */   
  978.                     *dtemp++ = ' ';
  979.                     break;
  980.                              
  981.              default:
  982.                        break;
  983.      }
  984.      *dtemp = '\0';         
  985.       
  986.            /* Get the length of the destination string and
  987.             * pad it with blanks to make it the width of 
  988.             * the final string wanted.
  989.             */
  990.          length = strlen( dindex );
  991.          for( i = length; i < PWIDTH( PopPtr ); i++ )
  992.             strcat( dindex, " " );
  993.             
  994.        }
  995.        /* Get the length of the actual string and
  996.         * advance it to the next string skipping the NULL
  997.         * and moving to the first char of the next string.
  998.         */
  999.        length = strlen( sindex );
  1000.        sindex = sindex + length + 1;
  1001.        
  1002.        /* Advance us thru the nulls to the start of the next string*/
  1003.        while( !(*sindex) )
  1004.          sindex++;
  1005.  
  1006.        /* Advance dest to end of the new string */   
  1007.        dindex = dindex + PWIDTH( PopPtr ) + 1;
  1008.      }
  1009.      else
  1010.      {
  1011.        /* The PNUM() menu item will have an UP Arrow in its text */
  1012.        strcpy( dindex, " " );
  1013.        for( i = 1; i < PWIDTH( PopPtr ); i++ )
  1014.           strcat( dindex, " " );  
  1015.        *(dindex + 2 ) = UP_ARROW;    /* UP ARROW */
  1016.           
  1017.        /* The PNUM() + 1 menu item will have a DOWN Arrow in its text */
  1018.        dindex = dindex + PWIDTH( PopPtr ) + 1;
  1019.        strcpy( dindex, " " );
  1020.        for( i = 1; i < PWIDTH( PopPtr ); i++ )
  1021.           strcat( dindex, " " );  
  1022.        *(dindex + 2 ) = DOWN_ARROW;    /* DOWN ARROW */       
  1023.        
  1024.        /* The PNUM() + 2 menu item will have a blank string in its text*/
  1025.        dindex = dindex + PWIDTH( PopPtr ) + 1;
  1026.        strcpy( dindex, " " );
  1027.        for( i = 1; i < PWIDTH( PopPtr ); i++ )
  1028.           strcat( dindex, " " );  
  1029.      }     
  1030.    }
  1031. }
  1032.  
  1033.  
  1034.  
  1035. /* CountBlanksNeeded()
  1036.  * ====================================================================
  1037.  * Counts the number of blanks to go between the menu item and the
  1038.  * control key or whatever.
  1039.  */
  1040. int
  1041. CountBlanksNeeded( char *dindex, char *dtemp, int width, int num )
  1042. {
  1043.     long length;
  1044.     
  1045.     *dtemp   = '\0';
  1046.     length = strlen( dindex );
  1047.     return( width - 1 - (int)length - num );
  1048. }
  1049.  
  1050.  
  1051.  
  1052.  
  1053. /* Pop_Arrow()
  1054.  *==========================================================================
  1055.  * Scroll the popup menu items if up or down arrow is selected.
  1056.  * 
  1057.  * IN: int obj:        up or down arrow is selected.
  1058.  *     int *offset:    Offset into the menu items text array
  1059.  *     int num_items:   Total number of menu items involved.
  1060.  *     char *items[]:   Pointer to the text array
  1061.  *
  1062.  * OUT: TRUE - ended on a submenu
  1063.  *    FALSE - ended on a normal menu item.
  1064.  */
  1065. BOOLEAN
  1066. Pop_Arrow( POP_PTR PopPtr, int obj )
  1067. {
  1068.    OBJECT *tree;
  1069.    int    DrawFlag;
  1070.    int    i,j;
  1071.    GRECT  rect;
  1072.    GRECT  xrect, clip;
  1073.    MRETS  mk;
  1074.    int    pxy[8];
  1075.    int    xclip[4];
  1076.    long   location = 0L;    /* SCREEN MFDB        */
  1077.    BOOLEAN DelayFlag;
  1078.    BOOLEAN ReturnFlag;
  1079.                  
  1080.    tree = POBJECT( PopPtr );
  1081.  
  1082.    xrect = ObRect( obj );
  1083.    objc_offset( tree, obj, &xrect.g_x, &xrect.g_y );
  1084.    DelayFlag  = TRUE;
  1085.    ReturnFlag = FALSE;
  1086.    
  1087.    do
  1088.    {
  1089.        BSTATE = 0;
  1090.        DrawFlag = FALSE;
  1091.        /* Up Arrow Selected AND not at the top */
  1092.        if( ( obj == PFIRST( PopPtr ) ) && ( POFFSET( PopPtr ) > 0 ))
  1093.        {
  1094.          POFFSET( PopPtr ) -= 1;
  1095.          DrawFlag = TRUE;
  1096.          /* special handling...to make it jump to zero if
  1097.           * we are displaying item 2, we can jump to zero, blit and
  1098.           * redraw the top 2 items instead.
  1099.           */
  1100.          if( POFFSET( PopPtr ) == 1 )
  1101.              POFFSET( PopPtr ) = 0;    
  1102.        }
  1103.  
  1104.        /* DOWN Arrow and not within PHEIGHT() items from the bottom */
  1105.        if( ( obj == PLAST( PopPtr ) ) && ( POFFSET( PopPtr ) <= ( PNUM( PopPtr ) - PHEIGHT( PopPtr ) ) ) )
  1106.        {
  1107.          POFFSET( PopPtr ) += 1;
  1108.          DrawFlag = TRUE;
  1109.          /* Special handling for first item only */
  1110.          if( POFFSET( PopPtr ) == 1 )
  1111.          {
  1112.            if( ( POFFSET( PopPtr ) + PHEIGHT( PopPtr ) - 1 ) < PNUM( PopPtr ) )
  1113.                  POFFSET( PopPtr ) += 1;
  1114.          }
  1115.        }
  1116.  
  1117.        j = POFFSET( PopPtr );
  1118.        if( DrawFlag )
  1119.        {
  1120.           UpArrowStatus( PopPtr );
  1121.             DownArrowStatus( PopPtr );      
  1122.  
  1123.           /* Get the Blit Rectangle */
  1124.           clip = ObRect( PFIRST( PopPtr ));
  1125.           objc_offset( tree, ROOT, &clip.g_x, &clip.g_y );
  1126.           clip.g_h *= PHEIGHT( PopPtr );
  1127.       
  1128.           /* Calculate the height */
  1129.       
  1130.           if( strchr( ObString( PFIRST( PopPtr ) ), UP_ARROW ) )
  1131.           {
  1132.              clip.g_h -= gl_hchar;    /* Subtract 1 char height     */
  1133.              clip.g_y += gl_hchar;    /* Move the Ypos down 1 level */ 
  1134.           }
  1135.       
  1136.           if( strchr( ObString( PLAST( PopPtr )), DOWN_ARROW ) )
  1137.           {
  1138.             clip.g_h -= gl_hchar;    /* Subtract 1 char height      */
  1139.           }
  1140.       
  1141.           /* Set up SOURCE rectangle */
  1142.           rc_intersect( &desk, &clip );
  1143.           rect = clip;
  1144.           rc_2xy( &clip, ( WORD *)&pxy[0] );   
  1145.  
  1146.           if( obj == PFIRST( PopPtr ) )
  1147.               clip.g_y += gl_hchar;
  1148.       
  1149.           if( obj == PLAST( PopPtr ) )
  1150.               clip.g_y -= gl_hchar;
  1151.           rc_intersect( &desk, &clip );
  1152.           rc_2xy( &clip, ( WORD *)&pxy[4] );   
  1153.  
  1154.           clip = rect;
  1155.           rc_2xy( &clip, ( WORD *)&xclip[0] );   
  1156.           vs_clip( vhandle, 1, xclip );
  1157.           graf_mouse( M_OFF, 0L );
  1158.           vro_cpyfm( vhandle, 3, pxy, ( MFDB *)&location, ( MFDB *)&location );
  1159.           graf_mouse( M_ON, 0L );
  1160.       
  1161.             for( i = PFIRST( PopPtr ); i <= PLAST( PopPtr ); i++ )
  1162.             {
  1163.              if( ObType( i ) == G_STRING )
  1164.              {
  1165.                 if( i == PFIRST( PopPtr ) && strchr( ObString( PFIRST( PopPtr )), UP_ARROW ))
  1166.                 {
  1167.                     ObString( i ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * PNUM( PopPtr ) ];
  1168.                     continue;
  1169.                 }
  1170.                     
  1171.                 if( i == PLAST( PopPtr ) &&  strchr( ObString( PLAST( PopPtr )), DOWN_ARROW ))
  1172.                 {
  1173.                     ObString( i ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) + 1 ) ];
  1174.                     continue;
  1175.                 }    
  1176.                  
  1177.               SetNormal( i );
  1178.               if( CmdState( PopPtr->CharPtr[j] ) & CHECKED )
  1179.                   CheckObj( i );
  1180.                     
  1181.                 if( CmdState( PopPtr->CharPtr[j] ) & DISABLED )
  1182.                     Disable( i );
  1183.                       
  1184.                 ObString( i ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * j++ ];
  1185.           }   
  1186.           }
  1187.   
  1188.       
  1189.           if( obj == PFIRST( PopPtr ) )
  1190.           {
  1191.              rect = ObRect( obj );
  1192.              objc_offset( tree, obj, &rect.g_x, &rect.g_y );
  1193.              if( strchr( ObString( obj ), UP_ARROW ) )
  1194.                  rect.g_y += gl_hchar;
  1195.              else
  1196.              {
  1197.                  rect.g_h += gl_hchar;
  1198.                  ObString( obj ) = &PTEXTBUFF( PopPtr )[ 0 ];
  1199.                  if( !(CmdState( PopPtr->CharPtr[ 0 ] ) & DISABLED) )
  1200.                    Select( obj );
  1201.                    
  1202.                  if( CmdState( PopPtr->CharPtr[ 0 ] ) & CHECKED )
  1203.                    CheckObj( obj );
  1204.              }   
  1205.           }     
  1206.       
  1207.           if( obj == PLAST( PopPtr ) )    
  1208.           {
  1209.              rect = ObRect( obj );
  1210.              objc_offset( tree, obj, &rect.g_x, &rect.g_y );
  1211.              rect.g_y -= gl_hchar;
  1212.              if( !strchr( ObString( obj ), DOWN_ARROW ) )
  1213.              {
  1214.            rect.g_h += gl_hchar;         
  1215.                ObString( obj ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) - 1 )];
  1216.                if( !(CmdState( PopPtr->CharPtr[ PNUM( PopPtr ) - 1 ] ) & DISABLED) )
  1217.                    Select( obj );
  1218.                if( CmdState( PopPtr->CharPtr[ PNUM( PopPtr ) - 1 ] ) & CHECKED )
  1219.                    CheckObj( obj );
  1220.              }   
  1221.           }
  1222.           Objc_draw( tree, ROOT, MAX_DEPTH, &rect ); 
  1223.        }
  1224.        else
  1225.        {
  1226.           if( ( obj == PFIRST( PopPtr ) )   &&
  1227.            ( !strchr( ObString( obj ), UP_ARROW )) &&
  1228.               ( !( CmdState( PopPtr->CharPtr[ 0 ] ) & DISABLED )) &&
  1229.               (  CmdFlag( PopPtr->CharPtr[0] ) == '~' )
  1230.             )
  1231.             ReturnFlag = TRUE;
  1232.  
  1233.           if( ( obj == PLAST( PopPtr ) )   &&
  1234.            ( !strchr( ObString( obj ), DOWN_ARROW ) ) &&
  1235.               ( !( CmdState( PopPtr->CharPtr[ PNUM( PopPtr ) - 1 ] ) & DISABLED )) &&
  1236.               (  CmdFlag( PopPtr->CharPtr[ PNUM( PopPtr ) - 1 ] ) == '~' )
  1237.             )
  1238.             ReturnFlag = TRUE;
  1239.        }
  1240.        
  1241.        if( DelayFlag )
  1242.        {
  1243.            Evnt_timer( CLICK_DELAY );
  1244.            DelayFlag = FALSE;
  1245.        }    
  1246.  
  1247.        Graf_mkstate( &mk );  
  1248.    }while( (( BSTATE == 1 ) || ( BREAL == 1 )) && ( xy_inrect( mk.x, mk.y, &xrect )));
  1249.  
  1250.    Evnt_button( 1, 1, 0, &mk );       /* Make sure we have an up button...*/
  1251.    return( ReturnFlag );
  1252. }
  1253.  
  1254.  
  1255.  
  1256.  
  1257. /* Pop_Blit()
  1258.  * ====================================================================
  1259.  * Blit from screen to buffer or buffer to screen for Popup Box redraws.
  1260.  *
  1261.  * IN: long *PopPtr:    Pointer to memory buffer...
  1262.  *     GRECT *clip:    GRECT of clip blit area
  1263.  *     int   flag:    0 - blit from screen to buffer
  1264.  *                      1 - blit from buffer to screen
  1265.  *
  1266.  * OUT: returns TRUE if successful, FALSE if failed.
  1267.  */
  1268. BOOLEAN
  1269. Pop_Blit( long *PopPtr, GRECT *xclip, int flag )
  1270. {
  1271.    long     location = 0L;    /* SCREEN MFDB        */
  1272.    int      nplanes;        /* Number of planes    */
  1273.    unsigned long size;        /* size of malloc    */
  1274.    int      pxy[8];        /* pxy for blit        */
  1275.    GRECT    clip;
  1276.    MFDB     PopMFDB;        /* buffer MFDB        */
  1277.  
  1278.    clip      = ( GRECT )*xclip;
  1279.    clip.g_x -= 2;        /* adjust the clip area to take care of*/
  1280.    clip.g_y -= 2;        /* the edges and shadows...           */
  1281.    clip.g_w += 5;
  1282.    clip.g_h += 5;   
  1283.    rc_2xy( &clip, ( WORD *)&pxy[0] );
  1284.    vs_clip( vhandle, 1, pxy ); 
  1285.  
  1286.    vq_extnd( vhandle, 1, work_out );
  1287.    nplanes = work_out[4];
  1288.  
  1289.    if( !flag )                    /* screen to buffer blit*/
  1290.    {
  1291.      size = (unsigned long)(((long)clip.g_w + 7L )/8L) *
  1292.             (long)clip.g_h * (long)nplanes;
  1293.      size *= 2L;       
  1294.      *PopPtr = (long )malloc( (unsigned long ) size );
  1295.    }  
  1296.    
  1297.    if( !*PopPtr )
  1298.          return( FALSE );
  1299.  
  1300.    PopMFDB.fd_addr     = (long *)*PopPtr;    /* Setup the MFDB      */
  1301.    PopMFDB.fd_w        = clip.g_w;            
  1302.    PopMFDB.fd_h        = clip.g_h;
  1303.    PopMFDB.fd_wdwidth   = ( clip.g_w + 15 ) / 16;
  1304.    PopMFDB.fd_stand     = 0;
  1305.    PopMFDB.fd_nplanes   = nplanes;
  1306.    PopMFDB.fd_r1     = PopMFDB.fd_r2 = PopMFDB.fd_r3 = 0;
  1307.    
  1308.    graf_mouse( M_OFF, 0L );
  1309.    if(!flag )            
  1310.    { 
  1311.      /* Screen to buffer blit */  
  1312.      rc_intersect( &desk, &clip );        
  1313.      rc_2xy( &clip, ( WORD *)&pxy[0] );
  1314.      pxy[4] = pxy[5] = 0;           
  1315.      pxy[6] = clip.g_w - 1;
  1316.      pxy[7] = clip.g_h - 1;
  1317.      vro_cpyfm( vhandle, 3, pxy, ( MFDB *)&location, &PopMFDB );
  1318.    }
  1319.    else                
  1320.    {
  1321.      /* Buffer to screen blit */
  1322.      rc_intersect( &desk, &clip );
  1323.      pxy[0] = pxy[1] = 0;           
  1324.      pxy[2] = clip.g_w - 1; 
  1325.      pxy[3] = clip.g_h - 1;
  1326.      rc_2xy( &clip, ( WORD *)&pxy[4] );   
  1327.      vro_cpyfm( vhandle, 3, pxy, &PopMFDB, ( MFDB *)&location );
  1328.      if( *PopPtr )
  1329.          free( (long *)*PopPtr );
  1330.      *PopPtr = 0L;    
  1331.    }
  1332.    graf_mouse( M_ON, 0L );
  1333.  
  1334.    return( TRUE );
  1335. }
  1336.  
  1337.  
  1338.  
  1339.  
  1340. /* UpArrowStatus()
  1341.  * =====================================================================
  1342.  */
  1343. void
  1344. UpArrowStatus( POP_PTR PopPtr )
  1345. {
  1346.   OBJECT *tree;
  1347.   GRECT  rect;
  1348.     
  1349.   tree = POBJECT( PopPtr );
  1350.     
  1351.   /* Check if we need to activate the UP ARROW or deactivate it */
  1352.   if( POFFSET( PopPtr ) )    /* display the UP ARROW */
  1353.   {
  1354.      if( !strchr( ObString( PFIRST( PopPtr )), UP_ARROW ) )
  1355.      {
  1356.           rect = ObRect( PFIRST( PopPtr ) );
  1357.           objc_offset( tree, PFIRST( PopPtr ), &rect.g_x, &rect.g_y );
  1358.           
  1359.           SetNormal( PFIRST( PopPtr ) );
  1360.           ObString( PFIRST( PopPtr ) ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * PNUM( PopPtr )  ];
  1361.           Objc_draw( tree, ROOT, MAX_DEPTH, &rect );
  1362.      }
  1363.   }
  1364.   else
  1365.     ObString( PFIRST( PopPtr ) ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) + 2 ) ];
  1366. }
  1367.  
  1368.  
  1369.  
  1370. /* DownArrowStatus()
  1371.  * =====================================================================
  1372.  */
  1373. void
  1374. DownArrowStatus( POP_PTR PopPtr )
  1375. {
  1376.   OBJECT *tree;
  1377.   GRECT  rect;
  1378.     
  1379.   tree = POBJECT( PopPtr );
  1380.   
  1381.   /* Check if we need to display the DOWN ARROW */
  1382.  
  1383.   /* DISPLAY the DOWN ARROW */
  1384.   if( ( POFFSET( PopPtr ) + PHEIGHT( PopPtr )) <= PNUM( PopPtr ) )
  1385.   {
  1386.       if( !strchr( ObString( PLAST( PopPtr )), DOWN_ARROW ) )
  1387.       {
  1388.         rect = ObRect( PLAST( PopPtr ) );
  1389.         objc_offset( tree, PLAST( PopPtr ), &rect.g_x, &rect.g_y );
  1390.  
  1391.         SetNormal( PLAST( PopPtr ) );
  1392.         ObString( PLAST( PopPtr ) ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) + 1 ) ];
  1393.         Objc_draw( tree, ROOT, MAX_DEPTH, &rect );
  1394.       }
  1395.   }
  1396.   else
  1397.     ObString( PLAST( PopPtr ) ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) + 2 ) ];
  1398. }
  1399.  
  1400.  
  1401.  
  1402.  
  1403. /* SetArrowClickDelay()
  1404.  * =====================================================================
  1405.  * Set the Click Delay before the menu starts to scroll.
  1406.  */
  1407. void
  1408. SetArrowClickDelay( long delay )
  1409. {
  1410.    CLICK_DELAY = (( delay > 0L ) ? ( delay ) : ( INIT_CLICK_DELAY ));
  1411. }
  1412.  
  1413.  
  1414.  
  1415.  
  1416. /* CHECK FOR KEYCODES...
  1417.  * =====================================================================
  1418.  * Checks the string for the keystrings below so that they may be
  1419.  * subtracted from the length of the string for calculation purposes.
  1420.  */
  1421.  
  1422. /* CheckForCtrl()
  1423.  * =====================================================================
  1424.  * Looks for a ^ in the string.
  1425.  * Returns a pointer to the string where it starts.
  1426.  */
  1427. char 
  1428. *CheckForCtrl( char *text )
  1429.    return( strchr( text, '^' ) );
  1430. }
  1431.  
  1432.  
  1433.  
  1434. /* CheckForAlt()
  1435.  * =====================================================================
  1436.  * Looks for a & in the string.
  1437.  * Returns a pointer to the string where it starts.
  1438.  */
  1439. char 
  1440. *CheckForAlt( char *text )
  1441. {
  1442.    return( strchr( text, '&' ) );
  1443. }
  1444.  
  1445.  
  1446. /* CheckForCapKey()
  1447.  * =====================================================================
  1448.  * Looks for a / in the string.
  1449.  * Returns a pointer to the string where it starts.
  1450.  */
  1451. char
  1452. *CheckForCapKey( char *text )
  1453. {
  1454.    return( strchr( text, '|' ) );
  1455. }
  1456.  
  1457.  
  1458. /* CheckForCmdKey()
  1459.  * =====================================================================
  1460.  * Looks for a $ in the string.
  1461.  * Returns pointer to the string where it starts.
  1462.  */
  1463. char
  1464. *CheckForCmdKey( char *text )
  1465. {
  1466.    return( strchr( text, '$' ) );
  1467. }
  1468.  
  1469.  
  1470. /* CheckForDisable()
  1471.  * =====================================================================
  1472.  * Looks for a @ in the string.
  1473.  * Returns TRUE if found, FALSE if not.
  1474.  */
  1475. BOOLEAN
  1476. CheckForDisable( char *text )
  1477. {
  1478.    return( ( BOOLEAN )strchr( text, '@' ) );
  1479. }
  1480.  
  1481.  
  1482. /* CheckForMenuCheck()
  1483.  * =====================================================================
  1484.  * Looks for a ! in the string.
  1485.  * Returns TRUE if found, FALSE if not.
  1486.  */
  1487. BOOLEAN
  1488. CheckForMenuCheck( char *text )
  1489. {
  1490.   return( ( BOOLEAN )strchr( text, '!' ) );
  1491. }
  1492.  
  1493.  
  1494.  
  1495. /* CheckForSubMenu()
  1496.  * =====================================================================
  1497.  * Looks for a ~ in the string.
  1498.  * Returns the pointer to the string where it starts.
  1499.  */
  1500. char
  1501. *CheckForSubMenu( char *text )
  1502. {
  1503.    return( strchr( text, '~' ) );
  1504. }
  1505.  
  1506.  
  1507.  
  1508. /* CheckForFuncKey()
  1509.  * =====================================================================
  1510.  * Looks for a # in the string.
  1511.  * Returns the pointer to the string if found.
  1512.  */
  1513. char
  1514. *CheckForFuncKey( char *text )
  1515. {
  1516.    return( strchr( text, '#' ) );
  1517. }
  1518.  
  1519.  
  1520. /* CheckForDuplicate()
  1521.  * =====================================================================
  1522.  * Looks for a * in the string.
  1523.  * Returns TRUE if found, FALSE if not.
  1524.  * The length of the string to compare should be ZERO. :-)
  1525.  * This is because this string will be the width of the menu.
  1526.  */
  1527. BOOLEAN
  1528. CheckForDuplicate( char *text )
  1529. {
  1530.    return( ( BOOLEAN )strchr( text, '*' ));
  1531. }
  1532.  
  1533.  
  1534.  
  1535. /* CheckForKeyCodes()
  1536.  * =====================================================================
  1537.  * Checks the text string for various keycodes and subtracts
  1538.  * an appropriate amount from the length if any are found.
  1539.  * For many of these, there should only be one of them.
  1540.  * We then ADD back in the amount of ACTUAL text usage this item
  1541.  * would take. IE: a Function Key can be F10 so 3 characters...
  1542.  * or a CmdKey can be the letters: 'DEL' 3 characters added.
  1543.  * This will also fill up the CMDCHAR structures.
  1544.  */
  1545. long
  1546. CheckForKeyCodes( POP_PTR PopPtr, char *text, int num_entry )
  1547. {
  1548.     long length;
  1549.     long num;
  1550.     
  1551.     length = strlen( text );
  1552.  
  1553.     if( strchr( text, '@' ))
  1554.        length -= 1L;
  1555.  
  1556.     if( strchr( text, '!' ))
  1557.        length -= 1L;
  1558.        
  1559.     switch( CmdFlag( PopPtr->CharPtr[ num_entry ] ) )
  1560.     {
  1561.        case '^':
  1562.        case '~':
  1563.        case '#':
  1564.        case '&': length += 1L;
  1565.                 break;
  1566.                 
  1567.        case '|': length += 2L;
  1568.                 break;
  1569.                 
  1570.        case '$': switch( CmdHotKey( PopPtr->CharPtr[ num_entry ] )  )
  1571.              {
  1572.                 case 'h': /* Home */
  1573.                           strcpy( &CmdText[0], KeyText[0] ); 
  1574.                          break;
  1575.                                  
  1576.                 case 'H': /* HELP */
  1577.                           strcpy( &CmdText[0], KeyText[1] );
  1578.                           break;
  1579.                                  
  1580.                 case 'U': /* Undo */
  1581.                             strcpy( &CmdText[0], KeyText[2] );
  1582.                             break;
  1583.                                  
  1584.                 case 'E': /* Escape*/
  1585.                             strcpy( &CmdText[0], KeyText[3] );
  1586.                             break;
  1587.                                  
  1588.                 case 'I': /* Insert */
  1589.                              strcpy( &CmdText[0], KeyText[4] );
  1590.                              break;
  1591.                                  
  1592.                 case 'C': /* Clr */
  1593.                             strcpy( &CmdText[0], KeyText[5] );
  1594.                             break;
  1595.                                  
  1596.                 case 'D': /* Delete */
  1597.                             strcpy( &CmdText[0], KeyText[6] );
  1598.                              break;
  1599.  
  1600.                 case 'T': /* TAB    */
  1601.                             strcpy( &CmdText[0], KeyText[7] );
  1602.                              break;
  1603.  
  1604.                 case 'e': /* ENTER  */
  1605.                             strcpy( &CmdText[0], KeyText[8] );
  1606.                              break;
  1607.  
  1608.                 case 'R': /* RETURN */        
  1609.                             strcpy( &CmdText[0], KeyText[9] );
  1610.                              break;
  1611.                    
  1612.                 default:  CmdText[0] = '\0';
  1613.                             break;
  1614.          }
  1615.          num = strlen( &CmdText[0] );
  1616.          num = num - 2 + 1;    /* minus the ctrl codes + 1 space */
  1617.          length += num;
  1618.                 break;
  1619.                 
  1620.        case '*': length = 1L;
  1621.                 break;
  1622.                       
  1623.        default:  
  1624.                 break;
  1625.     }
  1626.     return( length );
  1627. }
  1628.  
  1629. /* END OF KEYCODE CHECKING
  1630.  * =====================================================================
  1631.  */
  1632.  
  1633.  
  1634. /* CONTROLLING THE APPEARANCE OF ITEMS
  1635.  * =====================================================================
  1636.  */
  1637.  
  1638. /* SetHeight()
  1639.  * =====================================================================
  1640.  * Set the height of the Menu.( in chars ).
  1641.  */
  1642. void
  1643. SetHeight( int MenuID, int Height )
  1644. {
  1645.    POP_PTR PopPtr;
  1646.    
  1647.    PopPtr = GetMenuPtr( MenuID );
  1648.    
  1649.    if( Height > 0 )
  1650.       PHEIGHT( PopPtr ) = Height;
  1651. }
  1652.  
  1653.  
  1654.  
  1655. /* SetNumItems()
  1656.  * =====================================================================
  1657.  * Set the number of items in the menu. This can be used to display
  1658.  * fewer items than the InsertMenu() call created.
  1659.  */
  1660. void
  1661. SetNumItems( int MenuID, int NumItems )
  1662. {
  1663.    POP_PTR PopPtr;
  1664.   
  1665.    PopPtr = GetMenuPtr( MenuID );
  1666.    if( NumItems > 0 )
  1667.       PNUM( PopPtr ) = NumItems;
  1668. }
  1669.  
  1670.  
  1671.  
  1672. /* CheckItem()
  1673.  * =====================================================================
  1674.  * Places or removes a check mark at the left of a menu item.
  1675.  */
  1676. void
  1677. CheckItem( int MenuID, int item, BOOLEAN check )
  1678. {
  1679.     POP_PTR PopPtr;
  1680.     
  1681.     PopPtr = GetMenuPtr( MenuID );
  1682.     if( check )
  1683.       CmdState( PopPtr->CharPtr[ item ] ) |= CHECKED;
  1684.     else
  1685.       CmdState( PopPtr->CharPtr[ item ] ) &= ~CHECKED;
  1686. }
  1687.  
  1688.  
  1689.  
  1690. /* DisableItem()
  1691.  * =====================================================================
  1692.  * Disables a menu item.
  1693.  */
  1694. void
  1695. DisableItem( int MenuID, int item )
  1696. {
  1697.    POP_PTR PopPtr;
  1698.    
  1699.    PopPtr = GetMenuPtr( MenuID );
  1700.    CmdState( PopPtr->CharPtr[ item ] ) |= DISABLED;
  1701. }
  1702.  
  1703.  
  1704.  
  1705. /* EnableItem()
  1706.  * =====================================================================
  1707.  * Enables a menu item.
  1708.  */
  1709. void
  1710. EnableItem( int MenuID, int item )
  1711. {
  1712.    POP_PTR PopPtr;
  1713.    
  1714.    PopPtr = GetMenuPtr( MenuID );
  1715.    CmdState( PopPtr->CharPtr[ item ] ) &= ~DISABLED;
  1716. }
  1717.  
  1718.  
  1719. /* SetItemCmd()
  1720.  * =====================================================================
  1721.  * This can be used to attach a submenu to a menu item.
  1722.  * You must still call 'SetSubMenuID()' tell the popups which
  1723.  * submenu to attach. Otherwise, this is a great way to change
  1724.  * the keyboard shortcuts. Use SetItemMark() to change the
  1725.  * keyboard shortcuts AFTER setting the type with the SetItemCmd().
  1726.  */
  1727. void
  1728. SetItemCmd( int MenuID, int item, char cmd ) 
  1729. {
  1730.    POP_PTR PopPtr;
  1731.    
  1732.    PopPtr = GetMenuPtr( MenuID );
  1733.    CmdFlag( PopPtr->CharPtr[ item ] ) = cmd;
  1734. }
  1735.  
  1736.  
  1737. /* GetItemCmd()
  1738.  * =====================================================================
  1739.  * This can be used to determine if a submenu is attached to 
  1740.  * this menu item. It returns the current command.
  1741.  * RETURNS: 
  1742.  *           '~' == SubMenu for example
  1743.  */
  1744. char
  1745. GetItemCmd( int MenuID, int item )
  1746. {
  1747.     POP_PTR PopPtr;
  1748.  
  1749.     PopPtr = GetMenuPtr( MenuID );
  1750.     return( CmdFlag( PopPtr->CharPtr[ item ] ) );
  1751. }
  1752.  
  1753.  
  1754.  
  1755. /* SetSubMenuID()
  1756.  * =====================================================================
  1757.  * This can be used to change the ID of the submenu associated
  1758.  * with a menu item. Nothing happens of course, if the menu_item 
  1759.  * does not have a submenu CMD.
  1760.  */
  1761. void
  1762. SetSubMenuID( int MenuID, int item, int ID )
  1763. {
  1764.    POP_PTR PopPtr;
  1765.  
  1766.    PopPtr = GetMenuPtr( MenuID );
  1767.    if( GetItemCmd( MenuID, item ) == '~' )
  1768.      CmdSubMenu( PopPtr->CharPtr[ item ] ) = ID;
  1769. }
  1770.  
  1771.  
  1772. /* GetSubMenuID()
  1773.  * =====================================================================
  1774.  * This can be used to get the ID of the submenu associated
  1775.  * with the menu item. Course, the item must HAVE a submenu in
  1776.  * the first place. Use GetItemCmd to determine if there is a
  1777.  * submenu.
  1778.  * RETURN: -1 if there is no submenu.
  1779.  */
  1780. int
  1781. GetSubMenuID( int MenuID, int item )
  1782. {
  1783.    POP_PTR PopPtr;
  1784.  
  1785.    PopPtr = GetMenuPtr( MenuID );
  1786.    if( GetItemCmd( MenuID, item ) == '~' )
  1787.      return( CmdSubMenu( PopPtr->CharPtr[ item ] ));
  1788.    else
  1789.      return( -1 );  
  1790. }
  1791.  
  1792.  
  1793. /* SetItemMark()
  1794.  * =====================================================================
  1795.  * This can be used to change the CMDKEY of a keyboard shortcut.
  1796.  * The command type itself is set with SetItem(). All except function
  1797.  * keys and submenus.
  1798.  */
  1799. void
  1800. SetItemMark( int MenuID, int item, char key )
  1801. {
  1802.    POP_PTR PopPtr;
  1803.  
  1804.    PopPtr = GetMenuPtr( MenuID );
  1805.    CmdHotKey( PopPtr->CharPtr[ item ] ) = key;
  1806. }
  1807.  
  1808.  
  1809.  
  1810. /* GetItemMark() 
  1811.  * =====================================================================
  1812.  * This can be used to see what the CMDKEY shortcut is associated with
  1813.  * the menu item. All except function keys and submenus.
  1814.  */
  1815. char
  1816. GetItemMark( int MenuID, int item )
  1817. {
  1818.    POP_PTR PopPtr;
  1819.  
  1820.    PopPtr = GetMenuPtr( MenuID );
  1821.    return( CmdHotKey( PopPtr->CharPtr[ item ] ) );
  1822. }
  1823.  
  1824.  
  1825. /* SetFuncMark()
  1826.  * =====================================================================
  1827.  * This can be used to set the Functio key # that is to be
  1828.  * associated with the menu item.
  1829.  */
  1830. void
  1831. SetFuncMark( int MenuID, int item, int num )
  1832. {   
  1833.    POP_PTR PopPtr;
  1834.  
  1835.    PopPtr = GetMenuPtr( MenuID );
  1836.    CmdFuncKey( PopPtr->CharPtr[ item ] ) = num;
  1837. }
  1838.  
  1839.  
  1840.  
  1841.  
  1842. /* GetFuncMark()
  1843.  * =====================================================================
  1844.  * This can be used to get wot function key # is associated with
  1845.  * the menu item.
  1846.  */
  1847. int 
  1848. GetFuncMark( int MenuID, int item )
  1849. {
  1850.    POP_PTR PopPtr;
  1851.  
  1852.    PopPtr = GetMenuPtr( MenuID );
  1853.    return( CmdFuncKey( PopPtr->CharPtr[ item ] ) );
  1854. }
  1855.  
  1856.  
  1857.  
  1858. /* SetItem()
  1859.  * =====================================================================
  1860.  * Set the Menu item string.
  1861.  * 1) The string must not exceed the original string length.
  1862.  * 2) Meta characters will be printed as regular text.
  1863.  * 3) If the string is longer than the original, then
  1864.  *    the new string will be truncated.
  1865.  */
  1866. void
  1867. SetItem( int MenuID, int item, char *text )
  1868. {
  1869.    POP_PTR PopPtr;
  1870.    int     count;
  1871.    char    *txtptr;
  1872.    long    length;
  1873.    long    length2;
  1874.            
  1875.    PopPtr = GetMenuPtr( MenuID );
  1876.  
  1877.    txtptr = PTEXT( PopPtr );
  1878.    if( txtptr )
  1879.    {
  1880.        for( count = 0; count < item; count++ )
  1881.        {    
  1882.          length = strlen( txtptr );
  1883.          txtptr += ( length + 1 );
  1884.        }
  1885.        /* we've reached the text area that we want. */
  1886.        length = strlen( txtptr );
  1887.        strncpy( txtptr, text, length );
  1888.        length2 = strlen( txtptr );
  1889.        for( count = (int)length2; count < length; count++ )
  1890.           strcat( txtptr, " " );
  1891.    }
  1892. }
  1893.  
  1894.  
  1895.  
  1896. /* GetItem() 
  1897.  * =====================================================================
  1898.  * Returns the string associated with the menu item WITHOUT
  1899.  * the meta characters.
  1900.  */
  1901. char
  1902. *GetItem( int MenuID, int item )
  1903. {
  1904.    POP_PTR PopPtr;
  1905.    int     count;
  1906.    char    *txtptr;
  1907.    char    *sptr;
  1908.    char    *dptr;
  1909.    long    length;
  1910.             
  1911.    PopPtr = GetMenuPtr( MenuID );
  1912.  
  1913.    txtptr = PTEXT( PopPtr );
  1914.    if( txtptr )
  1915.    {
  1916.        for( count = 0; count < item; count++ )
  1917.        {    
  1918.          length = strlen( txtptr );
  1919.          txtptr += ( length + 1 );
  1920.        }
  1921.        
  1922.        /* we've reached the text area that we want. */
  1923.        length = strlen( txtptr );
  1924.        sptr = txtptr;
  1925.        dptr = &TempString[0];
  1926.        
  1927.        while( *sptr )
  1928.        {
  1929.        
  1930.           switch( *sptr )
  1931.           {
  1932.               case '*':
  1933.                  case '@':
  1934.               case '!': sptr++;
  1935.                     break;
  1936.                 
  1937.                  case '^':
  1938.               case '~':
  1939.                  case '&':
  1940.                  case '$':
  1941.                  case '|': sptr += 2;
  1942.                    break;
  1943.  
  1944.               case '#': sptr += 2;
  1945.                       length = strlen( sptr );
  1946.                       if( length > 1 )
  1947.                           sptr++;
  1948.                       break;
  1949.                       
  1950.                  default:  *dptr++ = *sptr++;
  1951.                            break;
  1952.            }
  1953.        }
  1954.        *dptr = '\0';
  1955.        return( ( char *)&TempString[0] );
  1956.    }
  1957.    return( (char *)NULL );
  1958. }
  1959.  
  1960.  
  1961.  
  1962. /* SetStartItem()
  1963.  * =====================================================================
  1964.  * This can be used to Set the start item on a submenu. This will
  1965.  * work of course, on the main menu too.
  1966.  */
  1967. void
  1968. SetStartItem( int MenuID, int item )
  1969. {
  1970.    POP_PTR PopPtr;
  1971.    
  1972.    PopPtr = GetMenuPtr( MenuID );
  1973.    PopUpItem( PopPtr ) = item;
  1974. }
  1975.  
  1976.  
  1977.  
  1978. /* GetStartItem()
  1979.  * =====================================================================
  1980.  * This can be used to get the start item in a submenu. Or at least
  1981.  * wot it currently thinks it is...
  1982.  */
  1983. int
  1984. GetStartItem( int MenuID )
  1985. {
  1986.    POP_PTR PopPtr;
  1987.    
  1988.    PopPtr = GetMenuPtr( MenuID );
  1989.    return( PopUpItem( PopPtr ) );
  1990. }
  1991.  
  1992.  
  1993.  
  1994. /* END OF APPEARANCE OF ITEMS
  1995.  * =====================================================================
  1996.  */
  1997.  
  1998.  
  1999. /* SUBMENU HANDLING
  2000.  * =====================================================================
  2001.  */
  2002.  
  2003.  
  2004.  
  2005. /* DoSubMenu()
  2006.  * =====================================================================
  2007.  * Display the submenu if necessary...
  2008.  *
  2009.  */
  2010. POP_PTR
  2011. DoSubMenu( POP_PTR PopPtr, int obj )
  2012. {
  2013.    int        num;
  2014.    POP_PTR SubPopPtr;
  2015.    GRECT   SubRect;   
  2016.    int     MenuID;
  2017.    OBJECT  *tree;
  2018.    int     curx;
  2019.    
  2020.  
  2021.    ActiveTree( POBJECT( PopPtr ) );
  2022.    SubPopPtr = ( POP_PTR )NULL;
  2023.    if( obj != -1 )
  2024.    {
  2025.      num = FindNum( PopPtr, obj );
  2026.      if( CmdFlag( PopPtr->CharPtr[ num ] ) == '~' )
  2027.      {
  2028.         SubRect   = ObRect( obj );
  2029.         objc_offset( tree, obj, &SubRect.g_x, &SubRect.g_y );
  2030.       
  2031.         MenuID    = CmdSubMenu( PopPtr->CharPtr[ num ] );
  2032.         SubPopPtr = GetMenuPtr( MenuID );
  2033.         /* Displays faster if drawn on a byte boundary.*/
  2034.         curx = SubRect.g_x + SubRect.g_w -1 - gl_wchar;
  2035.         curx = (( curx + 7 )/8)*8;
  2036.         if( !ShowSubMenu( MenuID, curx, SubRect.g_y, &SubRect ))
  2037.            SubPopPtr = ( POP_PTR )NULL;
  2038.      }
  2039.    }  
  2040.    return( SubPopPtr );
  2041. }
  2042.  
  2043.  
  2044.  
  2045.  
  2046. /* ShowSubMenu()
  2047.  * =====================================================================
  2048.  * Display the Initial Submenu 
  2049.  * RETURN: TRUE - AOK
  2050.  *         FALSE - Memory allocation error.
  2051.  */
  2052. BOOLEAN
  2053. ShowSubMenu( int MenuID, int xpos, int ypos, GRECT *rect )
  2054. {
  2055.     POP_PTR PopPtr;
  2056.     int     max_height;
  2057.            
  2058.     PopPtr = GetMenuPtr( MenuID );
  2059.  
  2060.     PPREV( PopPtr ) = ( POP_PTR )NULL;
  2061.         
  2062.     PXPOS( PopPtr )   = xpos;
  2063.     PYPOS( PopPtr )   = ypos;
  2064.  
  2065.     /* Take care of height of menu */ 
  2066.     if( PHEIGHT( PopPtr ) > PNUM( PopPtr ) )
  2067.         PHEIGHT( PopPtr ) = PNUM( PopPtr );
  2068.  
  2069.     /* Limit Height to Height of screen - ( 2 * gl_hchars ) */
  2070.     max_height = yres - ( 2 * gl_hchar );
  2071.     while( ( PHEIGHT( PopPtr ) * gl_hchar ) > max_height )
  2072.        PHEIGHT( PopPtr ) -= 1;
  2073.  
  2074.     PIX_HEIGHT( PopPtr )  = ( PHEIGHT( PopPtr ) * gl_hchar );
  2075.  
  2076.     /* Take care of the width of the menu here...*/
  2077.     PTEXTBUFFSIZE( PopPtr ) = CalcTextBufferSize( PopPtr );
  2078.     PIX_WIDTH( PopPtr )     = ( PWIDTH( PopPtr ) * gl_wchar );
  2079.  
  2080.     AdjustToScreen( PopPtr, xpos, ypos, rect, TRUE, FALSE );
  2081.  
  2082.     if( Pop_Blit( (long *)&PMEM( PopPtr ), &PObRect( PopPtr ), 0 ) )
  2083.     {
  2084.         if( Build_Objects( PopPtr ))
  2085.           return( TRUE );
  2086.     }    
  2087.     return( FALSE );
  2088. }
  2089.  
  2090.  
  2091.  
  2092.  
  2093. /* HideSubMenu()
  2094.  * =====================================================================
  2095.  * Hides the Submenu and free's any memory that it malloc'ed.
  2096.  */
  2097. void
  2098. HideSubMenu( POP_PTR PopPtr )
  2099. {
  2100.     Pop_Blit( (long *)&PMEM( PopPtr ), &PObRect( PopPtr ), 1 );
  2101.  
  2102.     if( POBJECT( PopPtr ) )
  2103.         free( POBJECT( PopPtr ) );
  2104.         
  2105.     if( PTEXTBUFF( PopPtr ) )    
  2106.         free( PTEXTBUFF( PopPtr ) );
  2107.         
  2108.     POBJECT( PopPtr )   = ( OBJECT *)NULL;
  2109.     PTEXTBUFF( PopPtr ) = ( char *)NULL;
  2110. }
  2111.  
  2112.  
  2113.  
  2114.  
  2115. /* EvntSubMenu()
  2116.  * ====================================================================
  2117.  * Submenu Handling...
  2118.  * IN: POP_PTR PopPtr
  2119.  * OUT: long      -1L   No Menu Items selected.
  2120.  *                -2L   We've rentered the submenu ( won't get to user )
  2121.  *                High Word         Low Word
  2122.  *                ---------        --------
  2123.  *                 Menu ID            -1      Selected Disabled Item
  2124.  *           Menu ID        Menu Item Selected Menu Item.
  2125.  */
  2126. long
  2127. EvntSubMenu( POP_PTR PopPtr )
  2128. {
  2129.    long   result;            /* Result to return with...     */
  2130.    MRETS  mk;                /* mouse structures          */
  2131.    int    oldobj;            /* old object              */
  2132.    int    done = FALSE;            /* Done flag to return..    */
  2133.    int    obj;                /* object mouse is over...      */
  2134.    int    event;            /* Mouse button event flag    */
  2135.                        /* TRUE = event occurred    */
  2136.    POP_PTR SubPopPtr;            /* Pointer to Submenu...        */
  2137.    POP_PTR TempPtr;            /* Temporary Submenu Pointer    */
  2138.    
  2139.    GRECT  SubRect;            /* ObRect of Submenu        */
  2140.    GRECT  TempRect;            /* Temp ObRect                */
  2141.    
  2142.    GRECT  wall;               /* ObRect for boundary checks   */
  2143.    GRECT  BoxRect;            /* ObRect for boundary checks   */
  2144.    GRECT  DragRect;            /* ObRect for Drag boundary     */
  2145.  
  2146.    OBJECT *tree;            /* Object tree declaration      */
  2147.    int    Action;            /* Action that is occurring     */
  2148.    int    num;                /* temp number storage        */
  2149.    int    ActiveObj;            /* Current Active Object        */
  2150.    
  2151.    long CurTimeHz;            /* Time in 200Hz...*/
  2152.    BOOLEAN MenuDelayFlag;        /* Display Submenu Delay in effect*/
  2153.    BOOLEAN MenuDragFlag;        /* Drag Submenu Delay in effect  */
  2154.    BOOLEAN DragStart;            /* Start Drag Submenu Delay...   */
  2155.    int  MenuObject;            /* Object to start the timer on*/            
  2156.    int  OldY;                /* Old Y pos for a drag */   
  2157.    int  OldX;
  2158.          
  2159.    ActiveTree( POBJECT( PopPtr ) );
  2160.    Graf_mkstate( &mk );
  2161.  
  2162.    oldobj     = -1;
  2163.    obj        = objc_find( tree, ROOT, MAX_DEPTH, mk.x, mk.y );
  2164.    SubPopPtr  = ( POP_PTR )NULL;
  2165.    BoxRect    = ObRect( ROOT );
  2166.    result     = -1L;
  2167.    OldY          = 0;
  2168.    OldX       = 0;
  2169.       
  2170.    MenuDelayFlag = FALSE;    
  2171.    MenuDragFlag  = FALSE;      
  2172.    DragStart     = FALSE;
  2173.    ActiveObj     = -1;
  2174.    BSTATE      = BREAL = 0;
  2175.  
  2176.    do
  2177.    {
  2178.        if( obj != -1 )
  2179.        {    
  2180.        if( obj != oldobj )
  2181.           {
  2182.          wall = ObRect( obj );
  2183.          objc_offset( tree, obj, &wall.g_x, &wall.g_y );
  2184.           if( SubPopPtr )
  2185.             {
  2186.             PPREV( SubPopPtr ) = ( POP_PTR )NULL;
  2187.             HideSubMenu( SubPopPtr );
  2188.             SubPopPtr = ( POP_PTR )NULL;
  2189.             MenuDragFlag  = FALSE;
  2190.             MenuDelayFlag = FALSE;
  2191.             DragStart     = FALSE;
  2192.             ActiveObj = -1;
  2193.          }
  2194.          if(( oldobj != -1 ) && ( oldobj != obj ))
  2195.               deselect( tree, oldobj );
  2196.  
  2197.          if( ( obj == PFIRST( PopPtr ) && strchr( ObString( obj ), UP_ARROW ) ) ||
  2198.              ( obj == PLAST( PopPtr ) && strchr( ObString( obj ), DOWN_ARROW ) )
  2199.            )
  2200.            deselect( tree, obj );  
  2201.          else
  2202.          {
  2203.             if( !IsDisabled( obj ) ) 
  2204.             {
  2205.                   select( tree, obj );
  2206.  
  2207.                if( ( num = FindNum( PopPtr, obj )) != -1 )
  2208.            {
  2209.               if( CmdFlag( PopPtr->CharPtr[ num ] ) == '~' )
  2210.               {
  2211.                  Supexec( GetTimeHz );
  2212.                  CurTimeHz     = TimeInHz;
  2213.                  MenuDelayFlag = TRUE;
  2214.                  MenuDragFlag  = FALSE;
  2215.                      DragStart     = FALSE;
  2216.                  MenuObject    = obj;
  2217.               }  
  2218.            }                
  2219.                }  
  2220.             }  
  2221.        }
  2222.        }
  2223.        else
  2224.        {
  2225.      if(( oldobj != -1 ) && ( oldobj != obj ) )
  2226.          {
  2227.         deselect( tree, oldobj );
  2228.          }
  2229.        }
  2230.        oldobj = obj;
  2231.        
  2232.        Graf_mkstate( &mk );
  2233.        OldY = mk.y;
  2234.        OldX = mk.x;
  2235.        do
  2236.        {
  2237.                BSTATE = 0;
  2238.             if( MenuDragFlag )
  2239.             {
  2240.                if( ( !xy_inrect( mk.x, mk.y, &wall ) || 
  2241.                    ( SubPopPtr && xy_inrect( mk.x, mk.y, &SubRect ) ))
  2242.                  )
  2243.                {
  2244.           if( DragStart )
  2245.           {
  2246.                     Supexec( GetTimeHz );
  2247.                     CurTimeHz = TimeInHz;
  2248.                     DragStart = FALSE;
  2249.                     DragRect.g_x = mk.x;
  2250.                     DragRect.g_y = SubRect.g_y;
  2251.                     DragRect.g_h = SubRect.g_h;
  2252.                     
  2253.                     OldX = mk.x;
  2254.                     OldY = mk.y;
  2255.                                 
  2256.                     if( mk.x <= SubRect.g_x )
  2257.                     {
  2258.                        DragRect.g_w = SubRect.g_w + ( SubRect.g_x - DragRect.g_x );
  2259.                     }
  2260.                     else
  2261.                     {
  2262.                DragRect.g_x = SubRect.g_x;
  2263.                        DragRect.g_w = mk.x - SubRect.g_x + 1;
  2264.                     }
  2265.                   }  
  2266.  
  2267.               if( SubPopPtr )
  2268.                   {
  2269.                        if( !DragStart )
  2270.                        {
  2271.                  Supexec( GetTimeHz );
  2272.                  if( ( TimeInHz - CurTimeHz ) >= SUBDRAG_DELAY )
  2273.                      MenuDragFlag = FALSE;
  2274.  
  2275.                  if( !xy_inrect( mk.x, mk.y, &DragRect ) ||
  2276.                      (xy_inrect( mk.x, mk.y, &DragRect ) &&
  2277.                       ( mk.x == OldX ) &&
  2278.                       ( mk.y != OldY )
  2279.                  ) 
  2280.                    )  
  2281.                    MenuDragFlag = FALSE;    
  2282.                  else
  2283.                  {
  2284.                         DragRect.g_x = mk.x;
  2285.                         if( mk.x <= SubRect.g_x )
  2286.                         {
  2287.                                   DragRect.g_w = SubRect.g_w + ( SubRect.g_x - DragRect.g_x );
  2288.                             }
  2289.                             else
  2290.                            {
  2291.                        DragRect.g_x = SubRect.g_x;
  2292.                                DragRect.g_w = mk.x - SubRect.g_x + 1;
  2293.                             }
  2294.                  }    
  2295.                }      
  2296.  
  2297.                        /* We're in the new submenu...*/
  2298.                        if( xy_inrect( mk.x, mk.y, &SubRect ) )
  2299.                        {
  2300.                          Action = 0;
  2301.                          MenuDragFlag = FALSE;
  2302.                          break;    
  2303.                        }    
  2304.                        MenuDelayFlag = FALSE;
  2305.                   }
  2306.                }
  2307.             }
  2308.             else
  2309.         {            
  2310.                 BoxRect = ObRect( ROOT );
  2311.                 if( xy_inrect( mk.x, mk.y, &BoxRect ) )
  2312.                 {
  2313.                        /* We're in our current submenu still
  2314.                      * but we might be in a new menu item.
  2315.                      */
  2316.                        Action = 1;
  2317.                        break;
  2318.                 }      
  2319.                 else           
  2320.                 {
  2321.                     /* Checking to see if we're in any of the previous menus */
  2322.                     TempPtr = PPREV( PopPtr );
  2323.                     while( TempPtr )
  2324.                     {
  2325.                        /* We're in a previous submenu or root */
  2326.                        TempRect = PObRect( TempPtr );
  2327.                        if( xy_inrect( mk.x, mk.y, &TempRect ) )
  2328.                        {
  2329.                           Action = 2;
  2330.                           goto abort;
  2331.                    }
  2332.                    TempPtr = PPREV( TempPtr );
  2333.                     }
  2334.                 }
  2335.                 
  2336.                 /* if we reach here, we're outside of all the rectangles.*/
  2337.             if( SubPopPtr )
  2338.             {
  2339.                    HideSubMenu( SubPopPtr );
  2340.                PPREV( SubPopPtr ) = ( POP_PTR )NULL;
  2341.                SubPopPtr     = ( POP_PTR )NULL;
  2342.                ActiveObj     = -1;
  2343.                MenuDragFlag  = FALSE;
  2344.                MenuDelayFlag = FALSE;
  2345.                 }
  2346.  
  2347.             if( oldobj != -1 )
  2348.             {
  2349.               deselect( tree, oldobj );
  2350.               oldobj = obj = -1;
  2351.             }  
  2352.             }
  2353.             Action = -1;
  2354.  
  2355.             OldX = mk.x;
  2356.             OldY = mk.y;
  2357.             Evnt_timer( 0L );         /* Kludge to put in a delay 
  2358.                               * so that we can get a new mouse
  2359.                               * x,y with > 1 pixel difference
  2360.                               */
  2361.             Graf_mkstate( &mk );
  2362.     }while( !BSTATE );
  2363. abort:
  2364.        /* we've exited due to a button click, OR
  2365.         * we've entered one of the rectangles.
  2366.         * Let's see wot we've got.
  2367.         * If Action == -1, then the user clicked outside of
  2368.         * ANY rectangles and we should just exit and close
  2369.         * down all of the menus.
  2370.         */
  2371.        Graf_mkstate( &mk );
  2372.        obj = objc_find( tree, ROOT, MAX_DEPTH, mk.x, mk.y );
  2373.  
  2374.        /* Fix for when the submenu is displayed and we click
  2375.         * on its title to exit.
  2376.         */
  2377.        if(  xy_inrect( mk.x, mk.y, &wall ) &&
  2378.             SubPopPtr && MenuDragFlag && ( BSTATE == 1 ) )
  2379.         Action = 1;            
  2380.           
  2381.        event = FALSE;
  2382.        switch( Action )
  2383.        {
  2384.           case 0:  /* Entered the New Submenu, if there is one */
  2385.                if( SubPopPtr )
  2386.                {
  2387.                   /* need to check wot is returned...*/
  2388.                   result = EvntSubMenu( SubPopPtr );
  2389.                   if(( result == -1L ) || ( result != -2L ) )
  2390.                   {
  2391.                      obj = -1;
  2392.                      event = TRUE;
  2393.                      goto leave;
  2394.                   }
  2395.                   TempPtr = SubPopPtr;   
  2396.                   PPREV( SubPopPtr ) = ( POP_PTR )NULL;
  2397.                   HideSubMenu( SubPopPtr );
  2398.                   SubPopPtr     = ( POP_PTR )NULL;
  2399.                   MenuDragFlag  = FALSE;
  2400.               MenuDelayFlag = FALSE;
  2401.  
  2402.               Graf_mkstate( &mk );
  2403.                       obj = objc_find( tree, ROOT, MAX_DEPTH, mk.x, mk.y );
  2404.                   num = FindNum( PopPtr, obj );
  2405.                      if( ( obj != -1 ) && ( ActiveObj == obj ) &&
  2406.                          ( CmdFlag( PopPtr->CharPtr[ num ] ) == '~' ) &&
  2407.                          ( GetMenuPtr( CmdSubMenu( PopPtr->CharPtr[ num ] )) == TempPtr )
  2408.                        )
  2409.               {
  2410.                   if( !IsSelected( obj ) )
  2411.                 select( tree, obj );
  2412.  
  2413.                   if( ( SubPopPtr = DoSubMenu( PopPtr, obj )) != ( POP_PTR )NULL )
  2414.                         {
  2415.                           ActiveObj = obj;
  2416.                       SubRect = PObRect( SubPopPtr );
  2417.               ActiveTree( POBJECT( PopPtr ) );
  2418.                       PPREV( SubPopPtr ) = PopPtr;
  2419.                           MenuDragFlag       = TRUE;
  2420.                           DragStart          = TRUE;
  2421.                    wall = ObRect( obj );
  2422.                   objc_offset( tree, obj, &wall.g_x, &wall.g_y );
  2423.                     }  
  2424.               }
  2425.               else
  2426.                 ActiveObj = -1;
  2427.                }     
  2428.                  break;
  2429.                   
  2430.           case 1:  /* we are still in the current submenu...*/
  2431.                    /* The user click on an arrow button? */
  2432.                   if( (( BSTATE == 1 ) || ( BREAL == 1 )) && (( obj == PFIRST( PopPtr)  && strchr( ObString( obj ), UP_ARROW )) ||
  2433.                        (( obj == PLAST( PopPtr ) ) && strchr( ObString( obj ), DOWN_ARROW ))
  2434.                      )) 
  2435.                    {
  2436.                     if( Pop_Arrow( PopPtr, obj ) )
  2437.                       {
  2438.                  if( ( num = FindNum( PopPtr, obj )) != -1 )
  2439.                  {
  2440.                    if( CmdFlag( PopPtr->CharPtr[ num ] ) == '~' )
  2441.                    {
  2442.                      Supexec( GetTimeHz );
  2443.                      CurTimeHz     = TimeInHz;
  2444.                      MenuDelayFlag = TRUE;
  2445.                      MenuObject    = obj;
  2446.                    }  
  2447.                  }                
  2448.                       }
  2449.                   }
  2450.                   else
  2451.                   {
  2452.                      if( !SubPopPtr && MenuDelayFlag && ( MenuObject == obj ))
  2453.                      {
  2454.                        Supexec( GetTimeHz );
  2455.                        if( ( TimeInHz - CurTimeHz ) >= SUBMENU_DELAY )
  2456.                        {
  2457.                             MenuDelayFlag = FALSE;
  2458.                  if( !IsSelected( obj ) )
  2459.                       select( tree, obj );
  2460.  
  2461.                       if( ( SubPopPtr = DoSubMenu( PopPtr, obj )) != ( POP_PTR )NULL )
  2462.                      {
  2463.                        ActiveObj = obj;
  2464.                            SubRect   = PObRect( SubPopPtr );
  2465.                    ActiveTree( POBJECT( PopPtr ) );
  2466.                            PPREV( SubPopPtr ) = PopPtr;
  2467.                            MenuDragFlag       = TRUE;
  2468.                            DragStart          = TRUE;
  2469.                        wall = ObRect( obj );
  2470.                        objc_offset( tree, obj, &wall.g_x, &wall.g_y );
  2471.                          }     
  2472.                            
  2473.                        }    
  2474.                      }
  2475.                      else
  2476.                        MenuDelayFlag = FALSE;
  2477.                            
  2478.                      /* we are just in another menu item.
  2479.                       * If a submenu was displayed, hide it.
  2480.                       */
  2481.               if( SubPopPtr && ( ActiveObj != obj ) )
  2482.               {
  2483.                     PPREV( SubPopPtr ) = ( POP_PTR )NULL;
  2484.                         HideSubMenu( SubPopPtr );
  2485.                     SubPopPtr     = ( POP_PTR )NULL;
  2486.                 MenuDragFlag  = FALSE;
  2487.                 MenuDelayFlag = FALSE;
  2488.                     ActiveObj     = -1;
  2489.                   }
  2490.                   
  2491.                   /* if the buttons are down, they clicked on something*/
  2492.                   if(( BSTATE == 1 ) || ( BREAL == 1 ))
  2493.                   {
  2494.                       event = TRUE;
  2495.                       
  2496.                           obj = objc_find( tree, ROOT, MAX_DEPTH, mk.x, mk.y );
  2497.                           
  2498.                           CurMenu   = PMENUID( PopPtr );
  2499.                           CurObject = obj;
  2500.                           result    = 0L;
  2501.                   }
  2502.                   }
  2503.                  break;
  2504.                  
  2505.           case 2:  /* we have entered a previous submenu...  */
  2506.            result = -2L;
  2507.  
  2508.           case -1: /* we have clicked outside of ANY submenus.*/
  2509.                  if( Action == -1 )
  2510.                  {
  2511.                     /* don't exit if not a left button click */
  2512.                     if( BSTATE != 1 )
  2513.                       goto leave;
  2514.                  }
  2515.                  CurMenu = CurObject = -1;
  2516.           default: event = TRUE;
  2517.            obj    = -1;
  2518.                  break;
  2519.        }
  2520.  
  2521. leave:              
  2522.        if( event )
  2523.        {
  2524.           if( SubPopPtr )
  2525.           {
  2526.              PPREV( SubPopPtr ) = ( POP_PTR )NULL;
  2527.              HideSubMenu( SubPopPtr );
  2528.                SubPopPtr = ( POP_PTR )NULL;
  2529.           }
  2530.       done = TRUE;
  2531.        }     
  2532.  
  2533.    }while( !done );
  2534.  
  2535.          
  2536.    if(( obj != -1 ) && ( result != -2L ) && ( result != -1L ) )
  2537.    {
  2538.       result = 0L;
  2539.       result = ( int )PMENUID( PopPtr );
  2540.       result = ( result << 16L );
  2541.       
  2542.       if( !IsDisabled( obj ) )
  2543.         obj = FindNum( PopPtr, obj );
  2544.       else
  2545.         obj = -1;
  2546.       result |= ( obj & 0x0000FFFF );
  2547.    }
  2548.    return( result );
  2549. }
  2550.  
  2551.  
  2552.  
  2553. /* FindNum()
  2554.  * =====================================================================
  2555.  * Given the object #, find the adjusted index of the menu item.
  2556.  */
  2557. int
  2558. FindNum( POP_PTR PopPtr, int obj )
  2559. {
  2560.     int offset;
  2561.     
  2562.     offset = POFFSET( PopPtr );
  2563.     if( offset > 0 )
  2564.         offset -= 1;
  2565.     obj -= PFIRST( PopPtr );
  2566.     obj += offset;    
  2567.     return( obj );    
  2568. }
  2569.  
  2570.  
  2571.  
  2572.  
  2573. /* MenuChoice()
  2574.  * =====================================================================
  2575.  * Returns the CURRENT values of the menu and object clicked on.
  2576.  * Call ONLY after the PopMenuSelect() call has returned.
  2577.  */
  2578. long
  2579. MenuChoice( void )
  2580. {
  2581.    long value;
  2582.    POP_PTR PopPtr;
  2583.    int obj;
  2584.    
  2585.    value = 0L;
  2586.    value = ( int )CurMenu;
  2587.    value = ( value << 16L );
  2588.    
  2589.    if( CurMenu != -1 )
  2590.    {
  2591.       PopPtr = GetMenuPtr( CurMenu );   
  2592.       obj = FindNum( PopPtr, CurObject );
  2593.    }     
  2594.    else
  2595.       obj = -1;
  2596.    value |= ( obj & 0x0000FFFF );
  2597.    return( value );
  2598. }
  2599.  
  2600.  
  2601. /* END OF SUBMENU ROUTINES
  2602.  * =====================================================================
  2603.  */
  2604.  
  2605.  
  2606.  
  2607.  
  2608.  
  2609. /* SUBMENU DELAY ROUTINES
  2610.  * =====================================================================
  2611.  */
  2612.  
  2613. /* GetTimeHz()
  2614.  * =====================================================================
  2615.  * Gets the System time in 20ms ticks.
  2616.  */
  2617. long
  2618. GetTimeHz( void )
  2619. {
  2620.    long *ptr;
  2621.    
  2622.    ptr = ( long *)0x4BA;
  2623.    TimeInHz = *ptr;
  2624.    return( TimeInHz );
  2625. }
  2626.  
  2627.  
  2628.  
  2629.  
  2630. /* SetSubMenuDelay()
  2631.  * =====================================================================
  2632.  * Sets the length of time before a submenu appears as the user drags
  2633.  * the pointer thru a menu list.
  2634.  * The Units are in # of Milliseconds. ( 1000 = 1 second ).
  2635.  */
  2636. void
  2637. SetSubMenuDelay( long ms )
  2638. {
  2639.    if( ms < 0L )
  2640.        ms = INIT_DISPLAY_DELAY;
  2641.    if( ms )    
  2642.        ms /= 20L;     
  2643.    SUBMENU_DELAY = ms;
  2644. }
  2645.  
  2646.  
  2647.  
  2648.  
  2649. /* SetSubDragDelay()
  2650.  * =====================================================================
  2651.  * Sets the length of time the user has to drag diagonally, a pointer
  2652.  * to an active submenu before the submenu closes.
  2653.  */
  2654. void
  2655. SetSubDragDelay( long ms )
  2656. {
  2657.    if( ms < 0L )
  2658.        ms = INIT_DRAG_DELAY;
  2659.    if( ms )    
  2660.        ms /= 20L;
  2661.    SUBDRAG_DELAY = ms;
  2662. }
  2663.  
  2664.  
  2665. /* END OF SUBMENU DELAY ROUTINES
  2666.  * =====================================================================
  2667.  */
  2668.  
  2669.  
  2670.  
  2671. /* AdjustToScreen()
  2672.  * =====================================================================
  2673.  * Takes the Submenu XY and adjusts it so that it fits within
  2674.  * the screen area still. If there is an optional object, we will
  2675.  * adjust around that.
  2676.  * If we DO adjust, we'll try to get it on a byte boundary.
  2677.  * BOOLEAN byte_flag - a flag to align the xpos on a byte boundary.
  2678.  *                   - TRUE - YES!
  2679.  *             - NOTE: if the menu does not want byte adjusting
  2680.  *                 BUT it is off the screen, we will adjust
  2681.  *                 it back ONTO the screen on a byte boundary.
  2682.  *                 We just won't byte_adjust it if its already
  2683.  *                 on the screen in its entirety.
  2684.  * BOOLEAN Display_Flag; - a flag for vertical alignment of submenus.
  2685.  *             - When the submenu extends BELOW the bottom
  2686.  *               of the screen, there are 2 ways to adjust
  2687.  *               the menu.
  2688.  *             1)Adjust by displaying the menu upwards vertically
  2689.  *               from the rectangle button.
  2690.  *             2)Adjust by moving the menu upwards 1 gl_hchar
  2691.  *               at a time until the menu does not extend
  2692.  *               below the screen boundary.
  2693.  *             TRUE - Use Version 1
  2694.  *             FALSE - Use Version 2
  2695.  */
  2696. void
  2697. AdjustToScreen( POP_PTR PopPtr, int xpos, int ypos, GRECT *rect, BOOLEAN byte_flag, BOOLEAN Display_Flag )
  2698. {
  2699.     int temp;
  2700.     int position;
  2701.            
  2702.     /* Handle the X Coordinate Adjustments...
  2703.      *------------------------------------------------------------------
  2704.      */
  2705.     temp = xpos + PIX_WIDTH( PopPtr );
  2706.     if( temp > ( xres - 4 ))
  2707.     {
  2708.         if( !byte_flag )
  2709.         {
  2710.           temp -= ( xres - 4 );
  2711.           temp = ( temp + gl_wchar )/gl_wchar;
  2712.           temp *= gl_wchar;
  2713.           xpos -= temp;
  2714.         }
  2715.         else
  2716.         {
  2717.           xpos = rect->g_x - PIX_WIDTH( PopPtr );
  2718.           xpos = ( xpos + gl_wchar )/gl_wchar;
  2719.           xpos *= gl_wchar;
  2720.         }        
  2721.     }
  2722.     else
  2723.     {
  2724.         if( byte_flag )
  2725.         {
  2726.           xpos = ( xpos + ( gl_wchar - 1 ) )/gl_wchar;
  2727.           xpos *= gl_wchar;
  2728.         }     
  2729.     }
  2730.     
  2731.     /* Handle X limits < 0 */
  2732.     while( xpos <= 0 )
  2733.        xpos += gl_wchar;
  2734.     PXPOS( PopPtr )   = xpos;
  2735.     
  2736.  
  2737.     /* Handle the Y Coordinate Adjustments...
  2738.      *------------------------------------------------------------------
  2739.      */
  2740.     /* Calculates the Position so that the submenu start item 
  2741.      * is adjacent to the button that activated it.
  2742.      */
  2743.     position = ( PopUpItem( PopPtr ) + ( PNUM( PopPtr ) > PHEIGHT( PopPtr ) )) / PHEIGHT( PopPtr );
  2744.     if( position )
  2745.     {
  2746.        position = PopUpItem( PopPtr );
  2747.        temp = PNUM( PopPtr ) - PHEIGHT( PopPtr ) + 1;
  2748.  
  2749.        if( position  >= temp )
  2750.            position   = temp;
  2751.        position -= 1;    
  2752.     }      
  2753.     position = PopUpItem( PopPtr ) - position;
  2754.     ypos = ypos - ( position * gl_hchar );
  2755.  
  2756.     
  2757.     /* Calculates a new position if the submenu exceeds the
  2758.      * height of the screen.
  2759.      */        
  2760.     temp = ypos + PIX_HEIGHT( PopPtr );
  2761.     if( temp > ( yres - 4 ))
  2762.     {
  2763.        if( Display_Flag )
  2764.           ypos = ypos - ( (PHEIGHT( PopPtr ) - position - 1 )* gl_hchar );        
  2765.        else
  2766.        {
  2767.           do
  2768.           {
  2769.              ypos -= gl_hchar;
  2770.              temp = ypos + PIX_HEIGHT( PopPtr );
  2771.           }while( temp > ( yres - 4 ) );
  2772.        }
  2773.     }
  2774.     
  2775.     while( ypos <= ( gl_hchar + ( gl_hchar/2 ) ))
  2776.        ypos += gl_hchar;
  2777.        
  2778.     PYPOS( PopPtr )   = ypos;
  2779. }
  2780.  
  2781.  
  2782.  
  2783.  
  2784. /* WaitForUpButton()
  2785.  * =====================================================================
  2786.  * Waits for an up button.
  2787.  */
  2788. void
  2789. WaitForUpButton( void )
  2790. {
  2791.   MRETS mk;
  2792.   
  2793.   do
  2794.   {
  2795.      Graf_mkstate( &mk );
  2796.   }while( mk.buttons );
  2797. }
  2798.  
  2799.  
  2800.